From: Philippe Lorin
Subject: Allocating C structs for lambda-GTK
Date: 
Message-ID: <42c65e3c$0$2438$626a14ce@news.free.fr>
I'm trying to use GtkTextIter and GtkTargetEntry structs with 
lambda-GTK. The problem is that GTK doesn't provide any means of 
creating them (AFAIK), so I need to allocate them.

I think I could be able to do that using something like alien:make-alien 
but I can't even find the name of the structs in lambda-GTK. This must 
be pretty obvious, but I've looked everywhere and found nothing. When I 
list symbols using do-external-symbols, the structs aren't there (no 
"rectangle" for instance).

Has anybody done something similar, or has any clue about how to find 
the desired symbols?

From: Keith Irwin
Subject: Re: Allocating C structs for lambda-GTK
Date: 
Message-ID: <1120412177.1742.15.camel@localhost.localdomain>
On Sat, 2005-07-02 at 11:29 +0200, Philippe Lorin wrote:
> I'm trying to use GtkTextIter and GtkTargetEntry structs with 
> lambda-GTK. The problem is that GTK doesn't provide any means of 
> creating them (AFAIK), so I need to allocate them.

Here's a bit of code I struggled with a year ago or so trying to get the
actual text from a text buffer:

(defun get-text (buf)
  (let ((end (gtk:gtk-alloc GtkTextIter))
        (start (gtk:gtk-alloc GtkTextIter)))
    (gtk:gtk-text-buffer-get-start-iter buf start)
    (gtk:gtk-text-buffer-get-end-iter buf end)
    (let ((text (gtk:gtk-text-buffer-get-text buf start end nil)))
      (let ((text-str (cvt-string text)))
       (gtk:gtk-free start)
       (gtk:gtk-free end)
       text-str))))

Alas, no proper exception handling, but then I abandoned the project.
I'm guessing gtk-alloc is what you want.  (Hoping.)

The c-to-list-string function was on the to do list for lambda-gtk when
I looked at it, so I had to write my own.  The following at least
worked.

(defun cvt-string (ptr)
  (let ((ptr* (sb-sys:int-sap ptr))
        (index 0)
        (result ()))
    (loop
       (let ((val (sb-sys:sap-ref-8 ptr* index)))
         (if (= val 0)
             (return)
             (progn
               (push (code-char val) result)
               (incf index)))))
    (coerce (nreverse result) 'string)))

Finally, I was interested in doing something with the text every time
the user hit a key, so:

(define-alien-function cb-keypress ((* t) (widget (* t)) (ev (* t)))
  (let ((buf (gtk:gtk-text-view-get-buffer widget)))
    (let ((text (get-text buf)))
      (format t "text=[~a] (len=~a)~%" text (length text)))))

This was using a patch to SBCL enabling callbacks.  I believe the patch
no longer works with recent versions of SBCL, but that callbacks will be
in the next release.

> I think I could be able to do that using something like alien:make-alien 
> but I can't even find the name of the structs in lambda-GTK. This must 
> be pretty obvious, but I've looked everywhere and found nothing. When I 
> list symbols using do-external-symbols, the structs aren't there (no 
> "rectangle" for instance).

Is the above:  (let ((end (gtk:gtk-alloc GtkTextIter)))) what you're
talking about?

> Has anybody done something similar, or has any clue about how to find 
> the desired symbols?

I've had luck finding things by going through the C documentation (and
I've never written anything significant in C.)  Ubuntu (debian?) has a
Devhelp package which provides a nice viewer.  I'd look stuff up there,
then grep through the lambda-gtk source.

Hope this helps!

Keith
From: ··········@bellsouth.net
Subject: Re: Allocating C structs for lambda-GTK
Date: 
Message-ID: <1120418737.618640.226040@o13g2000cwo.googlegroups.com>
Philippe Lorin wrote:
> I'm trying to use GtkTextIter and GtkTargetEntry structs with
> lambda-GTK. The problem is that GTK doesn't provide any means of
> creating them (AFAIK), so I need to allocate them.
>
> I think I could be able to do that using something like alien:make-alien
> but I can't even find the name of the structs in lambda-GTK. This must
> be pretty obvious, but I've looked everywhere and found nothing. When I
> list symbols using do-external-symbols, the structs aren't there (no
> "rectangle" for instance).
>
> Has anybody done something similar, or has any clue about how to find
> the desired symbols?

The relevant form here is: (gtk::struct-alloc gtktextiter)

(gtk:define-signal-handler delete-event :int (widget event data)
  widget event data
  (gtk:main-quit)
  gtk:+false+)

(gtk:define-signal-handler clicked-signal :void (view)
  (let ((iter (gtk::struct-alloc gtktextiter)) ; ***
	(buffer (gtk::text-view-get-buffer view)))
    (gtk::text-buffer-get-iter-at-line-index buffer iter 5 0)
    (gtk::text-view-scroll-to-iter view iter 0.0 gtk:+false+ 0.0 0.0)))

(defun doublethink ()
  (gtk:init-ensure)
  (let* ((window (gtk:window-new gtk::window-toplevel))
	 (view (gtk:text-view-new))
	 (buffer (gtk:text-view-get-buffer view))
	 (text "War is Peace
Freedom is Slavery
Ignorance is Strength

2 + 2 = 5")
	 (vbox (gtk:vbox-new gtk:+true+ 10))
	 (button (gtk:button-new-with-label "Scroll")))
    (g:signal-connect window "delete_event" (g:callback delete-event)
		      (g:nullptr))
    (g:signal-connect-swapped button "clicked"
			      (g:callback clicked-signal) view)
    (gtk::window-set-title window "DoubleThink")
    (gtk::container-set-border-width window 15)
    (gtk:container-add window vbox)
    (gtk:widget-show vbox)
    (gtk::text-buffer-set-text buffer text (length text))
    (gtk::widget-set-size-request view 150 60)
    (gtk:widget-show view)
    (gtk:box-pack-start vbox view gtk:+false+ gtk:+false+ 0)
    (gtk:box-pack-start vbox button  gtk:+false+ gtk:+false+ 0)
    (gtk:widget-show button)
    (gtk:widget-show window)
    (gtk:main)))

(doublethink)

Regards,

Nineteen84

(Caveat Emptor: this message has almost certainly been reviewed
and possibly modified by the Thought Police and related agencies.
Use at your own discretion.)
From: Philippe Lorin
Subject: Re: Allocating C structs for lambda-GTK
Date: 
Message-ID: <42ccfce8$0$10743$636a15ce@news.free.fr>
Thanks to Keith Irwin and Nineteen84, I was able to use GtkTextIter and
GtkTargetEntry. While the former was just a matter of knowing about
gtk:struct-alloc and using Keith's cstring->string, GtkTargetEntry was
not as easy. I could not find its definition in lambda-GTK. I tried
adding the following line to gtk.api, and processing this file as
indicated on the lambda-GTK homepage, but while gtktargetentry got
defined, I couldn't access its fields.
    (:struct "GtkTargetEntry" (:accessors "target" "flags" "info"))

So I explored the other way, less satisfying but which worked: using the
alien package. In case somebody is interested, I paste below a simple
example of drag'n'drop (written for CMUCL). It just creates two windows
and one button. You can drag the button to any droppable place that
accepts text/plain data, as well as drag text/plain data from anywhere
into the empty window. These actions will print stuff to the console,
not modify any widget.

Any comments welcome! Note that I'm a Lisp newbie, so the style is
probably not very good.


(load "../lambda-gtk-0.1/gtkffi-cmusbcl") ; adjust to your system
(gtk:init-ensure)

;;; Function by Keith Irwin on comp.lang.lisp; I changed "sb-sys" into "system" for CMUCL.
(defun cstring->string (ptr)
   (let ((ptr* (system:int-sap ptr))
         (index 0)
         (result ()))
     (loop
      (let ((val (system:sap-ref-8 ptr* index)))
        (if (= val 0)
            (return)
            (progn
              (push (code-char val) result)
              (incf index)))))
     (coerce (nreverse result) 'string)))

(defmacro dynamic-callback (&rest body)
   (let ((callback-name (gensym)))
     `(progn
       (gtk:define-signal-handler ,callback-name ,@body)
       (g:callback ,callback-name))))

;; Connects signal to widget with body as the callback.
(defmacro dynamic-signal-connect (widget signal &rest body)
   `(g:signal-connect ,widget ,signal
     (dynamic-callback ,@body)
     (g:nullptr)))

(defconstant GDK-BUTTON1-MASK 256)
(defconstant GDK-ACTION-COPY 2)
(defconstant GTK-DEST-DEFAULT-ALL 7)
(defconstant GDK-SELECTION-TYPE-STRING (gdk:atom-intern "GDK_SELECTION_TYPE_STRING" 0))
(defconstant text-to-send "This is the text sent!")

(alien:def-alien-type nil
     (alien:struct GtkTargetEntry
                   (target c-call:c-string)
                   (flags alien:signed 32)
                   (info alien:signed 32)))

(alien:with-alien ((target-entry (alien:struct GtkTargetEntry)))
   (setf (alien:slot target-entry 'target) "text/plain")
   (setf (alien:slot target-entry 'flags) 0)
   (setf (alien:slot target-entry 'info) 0)

   ;; Build source window
   (let ((win-source (gtk:window-new 0))
         (box (gtk:vbox-new 0 0))
         (dragable (gtk:button-new-with-label "Drag this Button")))
     (gtk:container-add win-source box)
     (gtk:container-add box dragable)

     (gtk:window-set-title win-source "Source")
     (dynamic-signal-connect win-source "delete-event" :void (widget event data)
                             (gtk:main-quit)
                             (quit))
     (gtk:drag-source-set dragable GDK-BUTTON1-MASK (alien:addr target-entry) 1 GDK-ACTION-COPY)
     (dynamic-signal-connect dragable "drag-data-get" :void (widget context data info time user-data)
                             ;; I don't know why the line below doesn't work
                             ;; (gtk:selection-data-set data GDK-SELECTION-TYPE-STRING 8 text-to-send (length text-to-send))
                             (gtk:selection-data-set-text data text-to-send (length text-to-send))
                             (format t "Text sent!~%"))
     (gtk:widget-show-all win-source))

   ;; Build destination window
   (let ((win-dest (gtk:window-new 0)))
     (gtk:window-set-title win-dest "Dest")
     (dynamic-signal-connect win-dest "delete-event" :void (widget event data)
                             (gtk:main-quit)
                             (quit))
     (gtk:drag-dest-set win-dest GTK-DEST-DEFAULT-ALL (alien:addr target-entry) 1 GDK-ACTION-COPY)
     (dynamic-signal-connect win-dest "drag-data-received" :void (widget context x y data info time user-data)
                             (format t "Text received: \"~A\"~%" (cstring->string (gtk:selection-data-get-text data))))
     (gtk:widget-show-all win-dest))

   (gtk:main))
From: ··········@bellsouth.net
Subject: Re: Allocating C structs for lambda-GTK
Date: 
Message-ID: <1120752368.564032.210780@g47g2000cwa.googlegroups.com>
Philippe Lorin wrote:
> Thanks to Keith Irwin and Nineteen84, I was able to use GtkTextIter and
> GtkTargetEntry. While the former was just a matter of knowing about
> gtk:struct-alloc and using Keith's cstring->string, GtkTargetEntry was
> not as easy. I could not find its definition in lambda-GTK. I tried
> adding the following line to gtk.api, and processing this file as
> indicated on the lambda-GTK homepage, but while gtktargetentry got
> defined, I couldn't access its fields.
>     (:struct "GtkTargetEntry" (:accessors "target" "flags" "info"))

Philippe,

Thanks for posting the drag-and-drop example. That will come in handy.

I just tried adding (:struct "GtkTargetEntry" (:accessors "target"
"flags" "info")) to gtk.api and rebuilding the system; it does work
as advertised.

Did you then use: (gtk::TargetEntry.target entry)?

On several occassions I have supplemented gtk.api and then assumed
that the accessor would be gtk::GtkTargetEntry.target instead of
gtk::TargetEntry.target.  That's a bit confusing, but I suppose
Rick is trying to conserve some keystrokes.

In any event, you might give it another try.

Regards,

Nineteen84
From: Philippe Lorin
Subject: Re: Allocating C structs for lambda-GTK
Date: 
Message-ID: <42cd772b$0$7603$626a14ce@news.free.fr>
··········@bellsouth.net wrote:
> I just tried adding (:struct "GtkTargetEntry" (:accessors "target"
> "flags" "info")) to gtk.api and rebuilding the system; it does work
> as advertised.

You're right, it does work. I was mislead by two things.

The first is that when using gtk:struct-alloc, the struct seems to be 
called GtkTargetEntry, while when accessing fields, it is called 
gtk:TargetEntry. I find this confusing; it took a while for me to figure 
out. Is there a rationale behind this?

The second is the difference between "::" and ":". I assumed there was 
no difference between the two, and I was wrongly using ":" exclusively 
-- now I think I understand that "::" is needed to access internal 
symbols. Sorry for this newbish mistake.


> On several occassions I have supplemented gtk.api and then assumed
> that the accessor would be gtk::GtkTargetEntry.target instead of
> gtk::TargetEntry.target.  That's a bit confusing, but I suppose
> Rick is trying to conserve some keystrokes.

I fall in the same trap about every time I look up some function name in 
the GTK docs and copy it into my code; I just add "gtk:" at the 
beginning and shortly after wish for a pair programmer to watch me 
typing ;) .


Now I am able to read a GtkTargetEntry with something like:
   (gtk:TargetEntry.info entry)
But this does not seem to be setfable; if I do:
   (setf (gtk:TargetEntry.info target-entry) 0)
I get:
   the function (SETF GTK:TARGETENTRY.INFO) is undefined.
still I can use:
   (setf (alien:slot target-entry 'gtk::info) 0)
but it's less satisfying. Did I miss something?

Finally, I'm struggling with yet another probably obvious thing: I can't 
find out how to setf a cstring field; I would write for instance:
   (setf (alien:slot target-entry 'gtk::target)
     (gtk:string->cstring "text/plain"))
But then when I try to read the field, with Keith's cstring->string, I 
get an error:
   #<Alien (* T) at #x080AAF40> is not of type (UNSIGNED-BYTE 32)
and without it, I get an alien:
   #<Alien (* T) at #x080AAF40>

For the record, I also tried not using gtk:string->cstring in the first 
place:
   (setf (alien:slot target-entry 'gtk::target) "text/plain")
but I get:
   "text/plain" fell through ETYPECASE expression.
   Wanted one of ((ALIEN:ALIEN #<ALIEN::ALIEN-POINTER-TYPE (* T)>)
                  SYSTEM:SYSTEM-AREA-POINTER NULL).

What should I do to set the target field properly?
From: ··········@bellsouth.net
Subject: Re: Allocating C structs for lambda-GTK
Date: 
Message-ID: <1120833121.110767.51560@g43g2000cwa.googlegroups.com>
Philippe Lorin wrote:

> Now I am able to read a GtkTargetEntry with something like:
>    (gtk:TargetEntry.info entry)
> But this does not seem to be setfable; if I do:
>    (setf (gtk:TargetEntry.info target-entry) 0)
> I get:
>    the function (SETF GTK:TARGETENTRY.INFO) is undefined.
> still I can use:
>    (setf (alien:slot target-entry 'gtk::info) 0)
> but it's less satisfying. Did I miss something?

I would prefer the (setf ) syntax myself, but currently you
set a slot like this:

(gtk:TargetEntry.info target-entry 0)

> Finally, I'm struggling with yet another probably obvious thing: I can't
> find out how to setf a cstring field; I would write for instance:
>    (setf (alien:slot target-entry 'gtk::target)
>      (gtk:string->cstring "text/plain"))
> But then when I try to read the field, with Keith's cstring->string, I
> get an error:
>    #<Alien (* T) at #x080AAF40> is not of type (UNSIGNED-BYTE 32)
> and without it, I get an alien:
>    #<Alien (* T) at #x080AAF40>

;; this is slightly different from Kevin's function
;; it expects a (* (unsigned 8))
(defun cstring->string (ptr)
  (do* (result
	(idx 0 (1+ idx))
	(char (deref ptr idx) (deref ptr idx)))
       ((= char 0) (coerce (nreverse result) 'string))
    (push (code-char char) result)))

(defun test ()
  (let ((entry (gtk::struct-alloc gtktargetentry))
	(str "Cabbages and Kings"))
    ;; set the value:
    (gtk::TargetEntry.target entry (gtk::string->cstring str))
    ;; now, fetch the value and display:
    (format t "~%recycled string: ~a~%"
	    (cstring->string
              (cast (gtk::TargetEntry.target entry) (* (unsigned
8)))))))

(test)

Hope this helps,

Nineteen84