+(defbinding (gtk-init "gtk_parse_args") () boolean
+ "Initializes the library without opening the display."
+ (nil null)
+ (nil null))
+
+(defun clg-init (&optional display multi-threading-p)
+ "Initializes the system and starts event handling."
+ (unless (gdk:display-get-default)
+ #?(pkg-exists-p "gtk+-2.0" :atleast-version "2.8.0")
+ (progn
+ #+sbcl(sb-int:set-floating-point-modes :traps nil)
+ #+cmu(ext:set-floating-point-modes :traps nil))
+
+ (gdk:gdk-init)
+ (unless (gtk-init)
+ (error "Initialization of GTK+ failed."))
+
+ (if (not multi-threading-p)
+ (%init-async-event-handling display)
+ #+sb-thread(%init-multi-threaded-event-handling display)
+ #-sb-thread(error "Multi threading not supported on this platform")))
+ (gdk:ensure-display display t))
+
+(defun clg-init-with-threading (&optional display)
+ (clg-init display t))
+
+
+#?(sbcl>= 1 0 6)
+;; A very minimal implementation of CLISP's socket-status
+(defun socket-status (socket seconds microseconds)
+ (sb-alien:with-alien ((read-fds (sb-alien:struct sb-unix:fd-set)))
+ (let ((fd (sb-sys:fd-stream-fd (car socket))))
+ (sb-unix:fd-zero read-fds)
+ (sb-unix:fd-set fd read-fds)
+
+ (let ((num-fds-changed
+ (sb-unix:unix-fast-select
+ (1+ fd) (sb-alien:addr read-fds) nil nil
+ seconds microseconds)))
+ (unless (or (not num-fds-changed) (zerop num-fds-changed))
+ (if (peek-char nil (car socket) nil)
+ :input
+ :eof))))))
+
+(defun %init-async-event-handling (display)
+ (let ((style #?(or (featurep :cmu) (sbcl< 1 0 6)) :fd-handler
+ #?-(or (featurep :cmu) (sbcl< 1 0 6)) nil))
+ (when (and
+ (find-package "SWANK")
+ (not (eq (symbol-value (find-symbol "*COMMUNICATION-STYLE*" "SWANK")) style)))
+ (error "When running clg in Slime, the communication style ~A must be used in combination with asynchronous event handling on this platform. See the README file and <http://common-lisp.net/project/slime/doc/html/slime_45.html> for more information." style)))
+
+ #?(or (featurep :cmu) (sbcl< 1 0 6))
+ (progn
+ (signal-connect (gdk:display-manager) 'display-opened
+ #'(lambda (display)
+ (let ((fd (gdk:display-connection-number display)))
+ (unless (< fd 0)
+ (let ((handler (add-fd-handler
+ (gdk:display-connection-number display)
+ :input #'main-iterate-all)))
+ (signal-connect display 'closed
+ #'(lambda (is-error-p)
+ (declare (ignore is-error-p))
+ (remove-fd-handler handler))))))))
+ (setq *periodic-polling-function* #'main-iterate-all)
+ (setq *max-event-to-sec* 0)
+ (setq *max-event-to-usec* *event-poll-interval*))
+
+ #+(and clisp readline)
+ ;; Readline will call the event hook at most ten times per second
+ (setf readline:event-hook #'main-iterate-all)
+
+ #?-(or (featurep :cmu) (sbcl< 1 0 6))
+ ;; When running in Slime we need to hook into the Swank server
+ ;; to handle events asynchronously.
+ (if (find-package "SWANK")
+ (let ((read-from-emacs (symbol-function (find-symbol "READ-FROM-EMACS" "SWANK")))
+ (stream (funcall (find-symbol "CONNECTION.SOCKET-IO" "SWANK") (symbol-value (find-symbol "*EMACS-CONNECTION*" "SWANK")))))
+ (setf (symbol-function (find-symbol "READ-FROM-EMACS" "SWANK"))
+ #'(lambda ()
+ (loop
+ (case (socket-status (cons stream :input) 0 *event-poll-interval*)
+ ((:input :eof) (return (funcall read-from-emacs)))
+ (otherwise (main-iterate-all)))))))
+ #-(and clisp readline)
+ (warn "Asynchronous event handling not supported on this platform. An explicit main loop has to be started."))
+
+ (gdk:display-open display))
+
+#+sb-thread
+(progn
+ (defvar *main-thread* nil)
+
+ ;; Hopefully, when threading support is added to the Win32 port of
+ ;; SBCL in the future, this will work just out of the box.
+ #+win32
+ (let ((done (sb-thread:make-waitqueue))
+ (functions ())
+ (results ()))
+
+ ;; In Win32 all GDK calls have to be made from the main loop
+ ;; thread, so we add a timeout function which will poll for code and
+ ;; execute it.
+
+ (defun funcall-in-main (function)
+ (if (or
+ (not *main-thread*)
+ (eq sb-thread:*current-thread* *main-thread*))
+ (funcall function)
+ (gdk:with-global-lock
+ (push function functions)
+ (sb-thread:condition-wait done gdk:*global-lock*)
+ (pop results))))
+
+ ;; Will lock REPL on error, need to be fixed!
+ (defun %funcall-in-main-poll ()
+ (when functions
+ (loop
+ for n from 0
+ while functions
+ do (push (funcall (pop functions)) results)
+ finally (sb-thread:condition-notify done n)))
+ t))
+
+ (defmacro within-main-loop (&body body)
+ #-win32 `(gdk:with-global-lock ,@body)
+ #+win32 `(funcall-in-main #'(lambda () ,@body)))
+
+ (defun %init-multi-threaded-event-handling (display)
+ (when (and
+ (find-package "SWANK")
+ (not (eq (symbol-value (find-symbol "*COMMUNICATION-STYLE*" "SWANK")) :spawn)))
+ (error "When running clg in Slime, the communication style :spawn must be used in combination with multi threaded event handling. See the README file and <http://common-lisp.net/project/slime/doc/html/slime_45.html> for more information."))
+ (gdk:threads-init)
+ (let ((main-running (sb-thread:make-waitqueue)))
+ (gdk:with-global-lock
+ (setf *main-thread*
+ (sb-thread:make-thread
+ #'(lambda ()
+ (gdk:with-global-lock
+ (gdk:display-open display)
+ #+win32(gdk:timeout-add-with-lock (/ *event-poll-interval* 1000)
+ #'%funcall-in-main-poll)
+ (sb-thread:condition-notify main-running)
+ (main)))
+ :name "gtk event loop"))
+ (sb-thread:condition-wait main-running gdk:*global-lock*)))
+
+ ;; We need to hook into the Swank server to protect calls to GDK properly.
+ ;; This will *only* protect code entered directly in the REPL.
+ (when (find-package "SWANK")
+ (push #'(lambda (form)
+ (within-main-loop (eval form)))
+ (symbol-value (find-symbol "*SLIME-REPL-EVAL-HOOKS*" "SWANK"))))))
+
+#-sb-thread
+(defmacro within-main-loop (&body body)
+ `(progn ,@body))
+
+
+
+;;; Generic functions
+
+(defgeneric add-to-radio-group (item1 item2))
+(defgeneric activate-radio-widget (item))
+(defgeneric (setf tool-item-tip-text) (tip-text tool-item))
+(defgeneric (setf tool-item-tip-private) (tip-private tool-item))
+
+
+
+;;; Misc
+
+(defbinding grab-add () nil
+ (widget widget))
+
+(defbinding grab-get-current () widget)
+
+(defbinding grab-remove () nil
+ (widget widget))
+
+(defbinding get-default-language () (copy-of pango:language))
+
+
+;;; About dialog
+
+#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0")
+(progn
+ (define-callback-marshal %about-dialog-activate-link-callback nil
+ (about-dialog (link string)))
+
+ (defbinding about-dialog-set-email-hook (function) nil
+ (%about-dialog-activate-link-callback callback)
+ ((register-callback-function function) unsigned-int)
+ (user-data-destroy-callback callback))
+
+ (defbinding about-dialog-set-url-hook (function) nil
+ (%about-dialog-activate-link-callback callback)
+ ((register-callback-function function) unsigned-int)
+ (user-data-destroy-callback callback)))
+
+
+;;; Acccel group
+
+(defbinding %accel-group-connect () nil
+ (accel-group accel-group)
+ (key unsigned-int)
+ (modifiers gdk:modifier-type)
+ (flags accel-flags)
+ (gclosure gclosure))
+
+(defun accel-group-connect (group accelerator function &optional flags)
+ (multiple-value-bind (key modifiers) (parse-accelerator accelerator)
+ (let ((gclosure (make-callback-closure function)))
+ (%accel-group-connect group key modifiers flags gclosure)
+ gclosure)))
+
+(defbinding accel-group-connect-by-path (group path function) nil
+ (group accel-group)
+ (path string)
+ ((make-callback-closure function) gclosure :in/return))
+
+(defbinding %accel-group-disconnect (group gclosure) boolean
+ (group accel-group)
+ (gclosure gclosure))
+
+(defbinding %accel-group-disconnect-key () boolean
+ (group accel-group)
+ (key unsigned-int)
+ (modifiers gdk:modifier-type))
+
+(defun accel-group-disconnect (group accelerator)
+ (etypecase accelerator
+ (gclosure (%accel-group-disconnect group accelerator))
+ (string
+ (multiple-value-bind (key modifiers) (parse-accelerator accelerator)
+ (%accel-group-disconnect-key group key modifiers)))))
+
+(defbinding %accel-group-query () (copy-of (vector (inlined accel-group-entry) n))
+ (accel-group accel-group)
+ (key unsigned-int)
+ (modifiers gdk:modifier-type)
+ (n int :out))
+
+(defun accel-group-query (accel-group accelerator)
+ (multiple-value-bind (key modifiers) (parse-accelerator accelerator)
+ (%accel-group-query accel-group key modifiers)))
+
+(defbinding %accel-group-activate () boolean
+ (accel-group accel-group)
+ (acceleratable gobject)
+ (key unsigned-int)
+ (modifiers gdk:modifier-type))
+
+(defun accel-group-activate (accel-group acceleratable accelerator)
+ (multiple-value-bind (key modifiers) (parse-accelerator accelerator)
+ (%accel-group-activate accel-group acceleratable key modifiers)))
+
+(defbinding accel-group-lock () nil
+ (accel-group accel-group))
+
+(defbinding accel-group-unlock () nil
+ (accel-group accel-group))
+
+(defbinding accel-group-from-accel-closure () accel-group
+ (closure gclosure))
+
+(defbinding %accel-groups-activate () boolean
+ (object gobject)
+ (key unsigned-int)
+ (modifiers gdk:modifier-type))
+
+(defun accel-groups-activate (object accelerator)
+ (multiple-value-bind (key modifiers) (parse-accelerator accelerator)
+ (%accel-groups-activate object key modifiers)))
+
+(defbinding accel-groups-from-object () (gslist accel-group)
+ (object gobject))
+
+(defbinding accelerator-valid-p (key &optional modifiers) boolean
+ (key unsigned-int)
+ (modifiers gdk:modifier-type))
+
+(defbinding %accelerator-parse () nil
+ (accelerator string)
+ (key unsigned-int :out)
+ (modifiers gdk:modifier-type :out))
+
+(defgeneric parse-accelerator (accelerator))
+
+(defmethod parse-accelerator ((accelerator string))
+ (multiple-value-bind (key modifiers) (%accelerator-parse accelerator)
+ (if (zerop key)
+ (error "Invalid accelerator: ~A" accelerator)
+ (values key modifiers))))
+
+(defmethod parse-accelerator ((accelerator cons))
+ (destructuring-bind (key modifiers) accelerator
+ (values
+ (etypecase key
+ (integer key)
+ (string
+ (or
+ (gdk:keyval-from-name key)
+ (error "Invalid key name: ~A" key)))
+ (character (parse-accelerator key)))
+ modifiers)))
+
+(defmethod parse-accelerator ((key integer))
+ key)
+
+(defmethod parse-accelerator ((key character))
+ (or
+ (gdk:keyval-from-name (string key))
+ (error "Invalid key name: ~A" key)))
+
+
+(defbinding accelerator-name () string
+ (key unsigned-int)
+ (modifiers gdk:modifier-type))
+
+#?(pkg-exists-p "gtk+-2.0" :atleast-version "2.6.0")
+(defbinding accelerator-get-label () string
+ (key unsigned-int)
+ (modifiers gdk:modifier-type))
+
+(defbinding %accelerator-set-default-mod-mask () nil
+ (default-modifiers gdk:modifier-type))
+
+(defun (setf accelerator-default-modifier-mask) (default-modifiers)
+ (%accelerator-set-default-mod-mask default-modifiers))
+
+(defbinding (accelerator-default-modifier-mask "gtk_accelerator_get_default_mod_mask") () gdk:modifier-type)