1 ! Copyright (C) 2005, 2009 Eduardo Cavazos and Slava Pestov
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors alien alien.c-types arrays ui ui.gadgets
4 ui.gestures ui.backend ui.clipboards ui.gadgets.worlds ui.render
5 ui.event-loop assocs kernel math namespaces opengl
6 sequences strings x11.xlib x11.events x11.xim x11.glx
7 x11.clipboard x11.constants x11.windows io.encodings.string
8 io.encodings.ascii io.encodings.utf8 combinators command-line
9 math.vectors classes.tuple opengl.gl threads math.rectangles
13 SINGLETON: x11-ui-backend
15 : XA_NET_WM_NAME ( -- atom ) "_NET_WM_NAME" x-atom ;
17 TUPLE: x11-handle-base glx ;
18 TUPLE: x11-handle < x11-handle-base xic window ;
19 TUPLE: x11-pixmap-handle < x11-handle-base pixmap glx-pixmap ;
21 C: <x11-handle> x11-handle
22 C: <x11-pixmap-handle> x11-pixmap-handle
24 M: world expose-event nip relayout ;
26 M: world configure-event
27 over configured-loc >>window-loc
28 swap configured-dim >>dim
29 ! In case dimensions didn't change
41 { HEX: FF08 "BACKSPACE" }
46 { HEX: FFFF "DELETE" }
52 { HEX: FF55 "PAGE_UP" }
53 { HEX: FF56 "PAGE_DOWN" }
67 : key-code ( keysym -- keycode action? )
68 dup key-codes at [ t ] [ 1string f ] ?if ;
70 : event-modifiers ( event -- seq )
71 XKeyEvent-state modifiers modifier ;
73 : valid-input? ( string gesture -- ? )
74 over empty? [ 2drop f ] [
75 mods>> { f { S+ } } member? [
76 [ [ 127 = not ] [ CHAR: \s >= ] bi and ] all?
78 [ [ 127 = not ] [ CHAR: \s >= ] [ alpha? not ] tri and and ] all?
82 : key-down-event>gesture ( event world -- string gesture )
84 handle>> xic>> lookup-string
85 [ swap event-modifiers ] dip key-code <key-down> ;
87 M: world key-down-event
88 [ key-down-event>gesture ] keep
89 [ propagate-key-gesture drop ]
90 [ 2over valid-input? [ nip user-input ] [ 3drop ] if ]
93 : key-up-event>gesture ( event -- gesture )
94 [ event-modifiers ] [ 0 XLookupKeysym key-code ] bi <key-up> ;
97 [ key-up-event>gesture ] dip propagate-key-gesture ;
99 : mouse-event>gesture ( event -- modifiers button loc )
101 [ XButtonEvent-button ]
105 M: world button-down-event
106 [ mouse-event>gesture [ <button-down> ] dip ] dip
109 M: world button-up-event
110 [ mouse-event>gesture [ <button-up> ] dip ] dip
113 : mouse-event>scroll-direction ( event -- pair )
114 XButtonEvent-button {
122 [ [ mouse-event>scroll-direction ] [ mouse-event-loc ] bi ] dip
125 M: world enter-event motion-event ;
127 M: world leave-event 2drop forget-rollover ;
129 M: world motion-event
130 [ [ XMotionEvent-x ] [ XMotionEvent-y ] bi 2array ] dip
131 move-hand fire-motion ;
133 M: world focus-in-event
135 dup handle>> xic>> XSetICFocus focus-world ;
137 M: world focus-out-event
139 dup handle>> xic>> XUnsetICFocus unfocus-world ;
141 M: world selection-notify-event
142 [ handle>> window>> selection-from-event ] keep
145 : supported-type? ( atom -- ? )
146 { "UTF8_STRING" "STRING" "TEXT" }
147 [ x-atom = ] with any? ;
149 : clipboard-for-atom ( atom -- clipboard )
151 { XA_PRIMARY [ selection get ] }
152 { XA_CLIPBOARD [ clipboard get ] }
156 : encode-clipboard ( string type -- bytes )
157 XSelectionRequestEvent-target
158 XA_UTF8_STRING = utf8 ascii ? encode ;
160 : set-selection-prop ( evt -- )
162 [ XSelectionRequestEvent-requestor ] keep
163 [ XSelectionRequestEvent-property ] keep
164 [ XSelectionRequestEvent-target ] keep
165 [ 8 PropModeReplace ] dip
167 XSelectionRequestEvent-selection
168 clipboard-for-atom contents>>
169 ] keep encode-clipboard dup length XChangeProperty drop ;
171 M: world selection-request-event
172 drop dup XSelectionRequestEvent-target {
173 { [ dup supported-type? ] [ drop dup set-selection-prop send-notify-success ] }
174 { [ dup "TARGETS" x-atom = ] [ drop dup set-targets-prop send-notify-success ] }
175 { [ dup "TIMESTAMP" x-atom = ] [ drop dup set-timestamp-prop send-notify-success ] }
176 [ drop send-notify-failure ]
179 M: x11-ui-backend (close-window) ( handle -- )
181 [ glx>> destroy-glx ]
182 [ window>> [ unregister-window ] [ destroy-window ] bi ]
185 M: world client-event
186 swap close-box? [ ungraft ] [ drop ] if ;
188 : gadget-window ( world -- )
189 [ [ window-loc>> ] [ dim>> ] bi glx-window ]
190 [ "Factor" create-xic ]
192 [ window>> register-window ] [ >>handle drop ] 2bi ;
194 : wait-event ( -- event )
195 QueuedAfterFlush events-queued 0 > [
197 None XFilterEvent 0 = [ drop wait-event ] unless
198 ] [ ui-wait wait-event ] if ;
200 M: x11-ui-backend do-events
201 wait-event dup XAnyEvent-window window dup
202 [ handle-event ] [ 2drop ] if ;
204 : x-clipboard@ ( gadget clipboard -- prop win )
206 find-world handle>> window>> ;
208 M: x-clipboard copy-clipboard
209 [ x-clipboard@ own-selection ] keep
212 M: x-clipboard paste-clipboard
213 [ find-world handle>> window>> ] dip atom>> convert-selection ;
215 : init-clipboard ( -- )
216 XA_PRIMARY <x-clipboard> selection set-global
217 XA_CLIPBOARD <x-clipboard> clipboard set-global ;
219 : set-title-old ( dpy window string -- )
220 dup [ 127 <= ] all? [ XStoreName drop ] [ 3drop ] if ;
222 : set-title-new ( dpy window string -- )
223 [ XA_NET_WM_NAME XA_UTF8_STRING 8 PropModeReplace ] dip
224 utf8 encode dup length XChangeProperty drop ;
226 M: x11-ui-backend set-title ( string world -- )
227 handle>> window>> swap
228 [ dpy get ] 2dip [ set-title-old ] [ set-title-new ] 3bi ;
230 M: x11-ui-backend set-fullscreen* ( ? world -- )
231 handle>> window>> "XClientMessageEvent" <c-object>
232 [ set-XClientMessageEvent-window ] keep
233 swap _NET_WM_STATE_ADD _NET_WM_STATE_REMOVE ?
234 over set-XClientMessageEvent-data0
235 ClientMessage over set-XClientMessageEvent-type
236 dpy get over set-XClientMessageEvent-display
237 "_NET_WM_STATE" x-atom over set-XClientMessageEvent-message_type
238 32 over set-XClientMessageEvent-format
239 "_NET_WM_STATE_FULLSCREEN" x-atom over set-XClientMessageEvent-data1
240 [ dpy get root get 0 SubstructureNotifyMask ] dip XSendEvent drop ;
242 M: x11-ui-backend (open-window) ( world -- )
244 handle>> window>> dup set-closable map-window ;
246 M: x11-ui-backend raise-window* ( world -- )
248 dpy get swap window>> XRaiseWindow drop
251 M: x11-handle select-gl-context ( handle -- )
253 [ window>> ] [ glx>> ] bi glXMakeCurrent
254 [ "Failed to set current GLX context" throw ] unless ;
256 M: x11-handle flush-gl-context ( handle -- )
257 dpy get swap window>> glXSwapBuffers ;
259 M: x11-pixmap-handle select-gl-context ( handle -- )
261 [ glx-pixmap>> ] [ glx>> ] bi glXMakeCurrent
262 [ "Failed to set current GLX context" throw ] unless ;
264 M: x11-pixmap-handle flush-gl-context ( handle -- )
267 M: x11-ui-backend (open-offscreen-buffer) ( world -- )
268 dup dim>> glx-pixmap <x11-pixmap-handle> >>handle drop ;
269 M: x11-ui-backend (close-offscreen-buffer) ( handle -- )
271 [ glx-pixmap>> glXDestroyGLXPixmap ]
272 [ pixmap>> XFreePixmap drop ]
273 [ glx>> glXDestroyContext ] 2tri ;
275 M: x11-ui-backend offscreen-pixels ( world -- alien w h )
276 [ [ dim>> ] [ handle>> pixmap>> ] bi pixmap-bits ] [ dim>> first2 ] bi ;
278 M: x11-ui-backend (with-ui) ( quot -- )
289 M: x11-ui-backend beep ( -- )
290 dpy get 100 XBell drop ;
292 x11-ui-backend ui-backend set-global
294 [ "DISPLAY" os-env "ui.tools" "listener" ? ]
295 main-vocab-hook set-global