bohonghuang / cl-gtk4

GTK4/Libadwaita/WebKit2 bindings for Common Lisp.
GNU Lesser General Public License v3.0
215 stars 9 forks source link

Can't properly pass "user_data" to callback for open file dialog #57

Closed kayprish closed 9 months ago

kayprish commented 9 months ago

I have a problem with trying to pass a list of existing widgets into a callback function for opening files. I assumed (mistakenly) that I could simply pass a reference to a lisp object as "data", but that fails with the following file (at line 40):


(defpackage #:iip-project
  (:use #:cl))

(in-package #:iip-project)

(gtk:define-application (:name iip-project
                         :id "org.kayprish.iip-project")
  (cffi:defcallback open-file-callback :void ((file-dialog :pointer)
                                              (result :pointer)
                                              ; data now contains (img-original img-new)
                                              (data :pointer))
    (setf file-dialog (gobj:pointer-object file-dialog 'gtk:file-dialog))
    (funcall
      (gtk::attach-restarts
        (lambda ()
          (alexandria:when-let ((img-original (first data))
                                (img-new      (second data))
                                (gfile        (ignore-errors
                                                (gtk:file-dialog-open-finish file-dialog result))))
            ; here do something with img-original and img-new
            )))))

  (gtk:define-main-window (window (gtk:make-application-window :application gtk:*application*))
    (let ((grid         (gtk:make-grid))
          (file-button  (gtk:make-button :label "Load file"))
          (img-original (gtk:make-image))
          (img-new      (gtk:make-image)))

      (gtk:connect file-button "clicked"
                   (lambda (button)
                     (let ((file-chooser  (gtk:make-file-dialog)))
                       (gtk:file-dialog-open file-chooser
                                             window
                                             nil
                                             (cffi:callback open-file-callback)
                                             ;;; THE FOLLOWING LINE CAUSES THE ERROR
                                             (list img-original img-new)))))

      (setf (gtk:window-child window) grid)

      (gtk:grid-attach grid img-original 0 1 1 1)
      (gtk:grid-attach grid file-button 0 2 1 1)

      (gtk:grid-attach grid img-new 2 1 1 1))
    (unless (gtk:widget-visible-p window)
      (gtk:window-present window))))

with the following error:

debugger invoked on a TYPE-ERROR @54C4E1AA in thread
#<THREAD tid=5038 "main thread" RUNNING {1001480093}>:
  The value
    (#<GIR::OBJECT-INSTANCE {100298FD33}> #<GIR::OBJECT-INSTANCE {100298FDE3}>)
  is not of type
    SB-SYS:SYSTEM-AREA-POINTER

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [RETURN                ] Return from current handler.
  1: [RETURN-AND-ABORT      ] Return from current handler and abort the GTK application.
  2: [RETURN-VALUE          ] Return from current handler with specified value.
  3: [RETURN-VALUE-AND-ABORT] Return from current handler with specified value and abort the GTK application.
  4: [ABORT                 ] Exit debugger, returning to top level.

((:METHOD GIR::MEM-SET (T T GIR::ARGUMENT-TYPE)) #.(SB-SYS:INT-SAP #X5FA0865B8C00) (#<GIR::OBJECT-INSTANCE {100298FD33}> #<GIR::OBJECT-INSTANCE {100298FDE3}>) #<GIR::ARGUMENT-TYPE {1002A5F6B3}>) [fast-method]
   source: (SETF (CFFI:MEM-REF
                  (CFFI:FOREIGN-SLOT-POINTER POS '(:UNION ARGUMENT) FIELD)
                  :POINTER)
                   VALUE)

I tried to wrap the list at line 40 of the code with (cffi:make-pointer (list ...)), but that also failed. I assume it's not relevant but the error occured with SBCL 2.4.0. I've tried to get the code down to a minumum working example, and the quickest way to reproduce the error is to run sbcl --eval '(ql:quickload "cl-gtk4")' --load iip-project.lisp --eval '(in-package :iip-project)' --eval '(iip-project)'

bohonghuang commented 9 months ago

user_data can only pass CFFI pointer. For the current workaround, please refer to issue #19.

kayprish commented 9 months ago

Thank you, I've looked into that issue and the other similar ones before, but I didn't immediately realize they were relevant for my problem, for anyone else facing the same issue, the fix I've ended up using is this one:

;;;; package.lisp

(defpackage #:iip-project
  (:use #:cl))

;;;; iip-project.lisp

(in-package #:iip-project)

(gtk:define-application (:name iip-project
                         :id "org.kayprish.iip-project")
  (cffi:defcallback open-file-callback :void ((file-dialog :pointer)
                                              (result :pointer)
                                              ; data now contains (img-original img-new)
                                              (data :pointer))
    (setf file-dialog (gobj:pointer-object file-dialog 'gtk:file-dialog))
    (setf data        (glib::get-object (cffi:pointer-address data)))
    (funcall
      (gtk::attach-restarts
        (lambda ()
          (alexandria:when-let ((img-original (first data))
                                (img-new      (second data))
                                (gfile        (ignore-errors
                                                (gtk:file-dialog-open-finish file-dialog result))))
            ; here do something with img-original and img-new
            )))))

  (gtk:define-main-window (window (gtk:make-application-window :application gtk:*application*))
    (let ((grid         (gtk:make-grid))
          (file-button  (gtk:make-button :label "Load file"))
          (img-original (gtk:make-image))
          (img-new      (gtk:make-image)))

      (gtk:connect file-button "clicked"
                   (lambda (button)
                     (let ((file-chooser  (gtk:make-file-dialog)))
                       (gtk:file-dialog-open file-chooser
                                             window
                                             nil
                                             (cffi:callback open-file-callback)
                                             ;;; THE FOLLOWING LINE CAUSES THE ERROR
                                             (cffi:make-pointer (glib::put-object (list img-original img-new)))))))

      (setf (gtk:window-child window) grid)

      (gtk:grid-attach grid img-original 0 1 1 1)
      (gtk:grid-attach grid file-button 0 2 1 1)

      (gtk:grid-attach grid img-new 2 1 1 1))
    (unless (gtk:widget-visible-p window)
      (gtk:window-present window))))

I'm closing this issue, thank you very much for the help bohonghuang, and thank you for writing this lovely binding!