andy128k / cl-gobject-introspection

BSD 2-Clause "Simplified" License
49 stars 15 forks source link

Fetching GAsyncResult from run-javascript #73

Closed Ambrevar closed 3 years ago

Ambrevar commented 4 years ago

I'm trying to run some javascript in a webview. To do this, I'm essentially implemented a simplified version of the example in the documentation: https://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebView.html#webkit-web-view-run-javascript-finish.

Full example:

(cl:defpackage #:gir-test-web
  (:use #:cl))
(in-package #:gir-test-web)

(defvar *gtk* (gir:ffi "Gtk"))
(defvar *gdk* (gir:ffi "Gdk"))
;; (defvar *glib* (gir:ffi "GLib"))
(defvar *gio* (gir:ffi "Gio"))
(defvar *webkit* (gir:require-namespace "WebKit2"))

(defvar *result* nil)
(defvar *callback-view* nil)

(cffi:defcallback javascript-result-callback :void ((gobject :pointer)
                                                    (gasyncres :pointer)
                                                    (user_data :pointer))
  (let ((res (gir::build-object-ptr (gir:nget *gio* "AsyncResult") gasyncres)))
    (let ((js-result (gir:invoke (*callback-view* 'run-javascript-finish)
                                 res)))
      (setf *result* (gir:invoke (js-result 'get-js-value))))))

(defun get-title (view)
  (setf *callback-view* view)
  (gir:invoke (view 'run-javascript)
              "document.title;"
              (cffi:null-pointer)
              (cffi:get-callback 'javascript-result-callback)
              (cffi:null-pointer)))

(defun event->key (ev)
  (let ((k (gir:field ev "keyval"))
        (state (gir:field ev "state"))
        (modifiers (list)))
    (dolist (mdef '((#b1    :shift)
                    (#b100  :ctrl)
                    (#b1000 :alt)))
      (destructuring-bind (int modifier)
          mdef
        (unless (zerop (logand int state))
          (push modifier modifiers))))
    (values (case k
              (65361 :left-arrow)
              (65362 :up-arrow)
              (65363 :right-arrow)
              (65364 :down-arrow)
              (otherwise k))
            modifiers)))

(defun main ()
  (gir:invoke (*gtk* 'init) nil)
  (let ((window (gir:invoke (*gtk* "Window" 'new)
                            (gir:nget *gtk* "WindowType" :toplevel)))
        (view (gir:invoke (*webkit* "WebView" 'new)))
        (button (gir:invoke (*gtk* "Button" 'new-with-label) "Hello, world!"))
        (button2 (gir:invoke (*gtk* "Button" 'new-with-label) "Dummy!"))
        (box (gir:invoke (*gtk* "Box" 'new)
                         (gir:nget *gtk* "Orientation" :vertical)
                         0)))
    (gir::g-signal-connect-data (gir::this-of window)
                                "destroy"
                                (cffi:foreign-symbol-pointer "gtk_main_quit")
                                (cffi:null-pointer)
                                (cffi:null-pointer)
                                0)
    (gir:connect window :key-press-event
                 (lambda (widget event)
                   (declare (ignore widget))
                   (let ((event-object
                           (gir::build-struct-ptr (gir:nget *gdk* "EventKey")
                                                  event)))
                     (case (nth-value 0 (event->key event-object))
                       (103 (get-title view))))))
    (gir:invoke (view 'load_uri) "https://gnu.org")
    (gir:invoke (box 'add) button)
    (gir:invoke (box 'pack-start) view t t 0)
    (gir:invoke (box 'add) button2)
    (gir:invoke (window 'add) box)
    (gir:invoke (window 'show-all))
    (gir:invoke (*gtk* 'main))))

Compile, switch to this package and call (main)). It should show a window with a webview. Pressing g should call the document.title; javascript. Sadly, *result* remains nil after the call, while I'd expect a JSCValue.

Any idea? And thanks for all the precious help you've given me so far :)

andy128k commented 4 years ago

I don't know much about WebKit and its API, but my guess is that JSCValue is not a part of Webkit but JavaScriptCore. Probably you need to load it also.

Ambrevar commented 4 years ago

Good point. I've added

(defvar javascript (gir:ffi "JavaScriptCore"))

but the result is still nil.

I'm realizing something: js-result (the result of run-javascript-finish) is a struct instance, so I guess I cannot call

(gir:invoke (js-result 'get-js-value))

because get-js-value is not a method and js-result is not an object.

Is this correct? If so, should GIR throw a warning/error?

Ambrevar commented 4 years ago

What are the recommended ways to introspect objects and repositories?

For instance:

jmercouris commented 3 years ago

What are the recommended ways to introspect objects and repositories? For instance: - List all repository functions / objects / methods. - Show the type / repository of an object / struct. - List the methods of an object.

@andy128k ?

jmercouris commented 3 years ago

The only way I've been able to figure out thus far is:

(gir:nget *gtk* "Window" 'new)

and then inspecting the object via SLIME

andy128k commented 3 years ago

Examples of inspection of a repository can be found in test files.

jmercouris commented 3 years ago

Thank you for the information!

andy128k commented 3 years ago

So, it was a bug which was fixed in #82.

Ambrevar commented 3 years ago

Fantastic! @jmercouris: Can you try and see if you can get the JavaScript result now?

andy128k commented 3 years ago

@Ambrevar @jmercouris I tested it already and it works as expected. *result* variable receives an object of type JSCValue. So, to get a text a call (gir:invoke (*result*) 'to_string) should be added.