renaming: contain? -> any?, deep-contains? -> deep-any?, pad-left -> pad-head, pad...
[factor/jcg.git] / basis / ui / x11 / x11.factor
blob34cff4277790d35a405836b148c3cb12ae1a375f
1 ! Copyright (C) 2005, 2008 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 sequences
6 strings x11.xlib x11.events x11.xim x11.glx x11.clipboard
7 x11.constants x11.windows io.encodings.string io.encodings.ascii
8 io.encodings.utf8 combinators command-line
9 math.vectors classes.tuple opengl.gl threads math.geometry.rect
10 environment ascii ;
11 IN: ui.x11
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
30     relayout-1 ;
32 : modifiers
33     {
34         { S+ HEX: 1 }
35         { C+ HEX: 4 }
36         { A+ HEX: 8 }
37     } ;
38     
39 : key-codes
40     H{
41         { HEX: FF08 "BACKSPACE" }
42         { HEX: FF09 "TAB"       }
43         { HEX: FF0D "RET"       }
44         { HEX: FF8D "ENTER"     }
45         { HEX: FF1B "ESC"       }
46         { HEX: FFFF "DELETE"    }
47         { HEX: FF50 "HOME"      }
48         { HEX: FF51 "LEFT"      }
49         { HEX: FF52 "UP"        }
50         { HEX: FF53 "RIGHT"     }
51         { HEX: FF54 "DOWN"      }
52         { HEX: FF55 "PAGE_UP"   }
53         { HEX: FF56 "PAGE_DOWN" }
54         { HEX: FF57 "END"       }
55         { HEX: FF58 "BEGIN"     }
56         { HEX: FFBE "F1"        }
57         { HEX: FFBF "F2"        }
58         { HEX: FFC0 "F3"        }
59         { HEX: FFC1 "F4"        }
60         { HEX: FFC2 "F5"        }
61         { HEX: FFC3 "F6"        }
62         { HEX: FFC4 "F7"        }
63         { HEX: FFC5 "F8"        }
64         { HEX: FFC6 "F9"        }
65     } ;
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?
77         ] [
78             [ [ 127 = not ] [ CHAR: \s >= ] [ alpha? not ] tri and and ] all?
79         ] if
80     ] if ;
82 : key-down-event>gesture ( event world -- string gesture )
83     dupd
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 ]
91     3bi ;
93 : key-up-event>gesture ( event -- gesture )
94     dup event-modifiers swap 0 XLookupKeysym key-code <key-up> ;
96 M: world key-up-event
97     [ key-up-event>gesture ] dip propagate-key-gesture ;
99 : mouse-event>gesture ( event -- modifiers button loc )
100     [ event-modifiers ]
101     [ XButtonEvent-button ]
102     [ mouse-event-loc ]
103     tri ;
105 M: world button-down-event
106     [ mouse-event>gesture [ <button-down> ] dip ] dip
107     send-button-down ;
109 M: world button-up-event
110     [ mouse-event>gesture [ <button-up> ] dip ] dip
111     send-button-up ;
113 : mouse-event>scroll-direction ( event -- pair )
114     XButtonEvent-button {
115         { 4 { 0 -1 } }
116         { 5 { 0 1 } }
117         { 6 { -1 0 } }
118         { 7 { 1 0 } }
119     } at ;
121 M: world wheel-event
122     [ [ mouse-event>scroll-direction ] [ mouse-event-loc ] bi ] dip
123     send-wheel ;
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
134     nip
135     dup handle>> xic>> XSetICFocus focus-world ;
137 M: world focus-out-event
138     nip
139     dup handle>> xic>> XUnsetICFocus unfocus-world ;
141 M: world selection-notify-event
142     [ handle>> window>> selection-from-event ] keep
143     user-input ;
145 : supported-type? ( atom -- ? )
146     { "UTF8_STRING" "STRING" "TEXT" }
147     [ x-atom = ] with any? ;
149 : clipboard-for-atom ( atom -- clipboard )
150     {
151         { XA_PRIMARY [ selection get ] }
152         { XA_CLIPBOARD [ clipboard get ] }
153         [ drop <clipboard> ]
154     } case ;
156 : encode-clipboard ( string type -- bytes )
157     XSelectionRequestEvent-target
158     XA_UTF8_STRING = utf8 ascii ? encode ;
160 : set-selection-prop ( evt -- )
161     dpy get swap
162     [ XSelectionRequestEvent-requestor ] keep
163     [ XSelectionRequestEvent-property ] keep
164     [ XSelectionRequestEvent-target ] keep
165     [ 8 PropModeReplace ] dip
166     [
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 ]
177     } cond ;
179 M: x11-ui-backend (close-window) ( handle -- )
180     dup xic>> XDestroyIC
181     dup glx>> destroy-glx
182     window>> dup unregister-window
183     destroy-window ;
185 M: world client-event
186     swap close-box? [ ungraft ] [ drop ] if ;
188 : gadget-window ( world -- )
189     dup window-loc>> over rect-dim glx-window
190     over "Factor" create-xic rot <x11-handle>
191     2dup window>> register-window
192     >>handle drop ;
194 : wait-event ( -- event )
195     QueuedAfterFlush events-queued 0 > [
196         next-event dup
197         None XFilterEvent zero? [ drop wait-event ] unless
198     ] [
199         ui-wait wait-event
200     ] if ;
202 M: x11-ui-backend do-events
203     wait-event dup XAnyEvent-window window dup
204     [ handle-event ] [ 2drop ] if ;
206 : x-clipboard@ ( gadget clipboard -- prop win )
207     atom>> swap
208     find-world handle>> window>> ;
210 M: x-clipboard copy-clipboard
211     [ x-clipboard@ own-selection ] keep
212     (>>contents) ;
214 M: x-clipboard paste-clipboard
215     [ find-world handle>> window>> ] dip atom>> convert-selection ;
217 : init-clipboard ( -- )
218     XA_PRIMARY <x-clipboard> selection set-global
219     XA_CLIPBOARD <x-clipboard> clipboard set-global ;
221 : set-title-old ( dpy window string -- )
222     dup [ 127 <= ] all? [ XStoreName drop ] [ 3drop ] if ;
224 : set-title-new ( dpy window string -- )
225     [ XA_NET_WM_NAME XA_UTF8_STRING 8 PropModeReplace ] dip
226     utf8 encode dup length XChangeProperty drop ;
228 M: x11-ui-backend set-title ( string world -- )
229     handle>> window>> swap
230     [ dpy get ] 2dip [ set-title-old ] [ set-title-new ] 3bi ;
232 M: x11-ui-backend set-fullscreen* ( ? world -- )
233     handle>> window>> "XClientMessageEvent" <c-object>
234     tuck set-XClientMessageEvent-window
235     swap _NET_WM_STATE_ADD _NET_WM_STATE_REMOVE ?
236     over set-XClientMessageEvent-data0
237     ClientMessage over set-XClientMessageEvent-type
238     dpy get over set-XClientMessageEvent-display
239     "_NET_WM_STATE" x-atom over set-XClientMessageEvent-message_type
240     32 over set-XClientMessageEvent-format
241     "_NET_WM_STATE_FULLSCREEN" x-atom over set-XClientMessageEvent-data1
242     [ dpy get root get 0 SubstructureNotifyMask ] dip XSendEvent drop ;
244 M: x11-ui-backend (open-window) ( world -- )
245     dup gadget-window
246     handle>> window>> dup set-closable map-window ;
248 M: x11-ui-backend raise-window* ( world -- )
249     handle>> [
250         dpy get swap window>> XRaiseWindow drop
251     ] when* ;
253 M: x11-handle select-gl-context ( handle -- )
254     dpy get swap
255     [ window>> ] [ glx>> ] bi glXMakeCurrent
256     [ "Failed to set current GLX context" throw ] unless ;
258 M: x11-handle flush-gl-context ( handle -- )
259     dpy get swap window>> glXSwapBuffers ;
261 M: x11-pixmap-handle select-gl-context ( handle -- )
262     dpy get swap
263     [ glx-pixmap>> ] [ glx>> ] bi glXMakeCurrent
264     [ "Failed to set current GLX context" throw ] unless ;
266 M: x11-pixmap-handle flush-gl-context ( handle -- )
267     drop ;
269 M: x11-ui-backend (open-offscreen-buffer) ( world -- )
270     dup dim>> glx-pixmap <x11-pixmap-handle> >>handle drop ;
271 M: x11-ui-backend (close-offscreen-buffer) ( handle -- )
272     dpy get swap
273     [ glx-pixmap>> glXDestroyGLXPixmap ]
274     [ pixmap>> XFreePixmap drop ]
275     [ glx>> glXDestroyContext ] 2tri ;
277 M: x11-ui-backend offscreen-pixels ( world -- alien w h )
278     [ [ dim>> ] [ handle>> pixmap>> ] bi pixmap-bits ] [ dim>> first2 ] bi ;
280 M: x11-ui-backend ui ( -- )
281     [
282         f [
283             [
284                 init-clipboard
285                 start-ui
286                 event-loop
287             ] with-xim
288         ] with-x
289     ] ui-running ;
291 M: x11-ui-backend beep ( -- )
292     dpy get 100 XBell drop ;
294 x11-ui-backend ui-backend set-global
296 [ "DISPLAY" os-env "ui" "listener" ? ]
297 main-vocab-hook set-global