]> gitweb.factorcode.org Git - factor.git/blob - basis/ui/x11/x11.factor
Merge branch 'master' of git://repo.or.cz/factor/jcg
[factor.git] / basis / ui / x11 / x11.factor
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 assocs kernel math namespaces opengl sequences strings x11.xlib
6 x11.events x11.xim x11.glx x11.clipboard x11.constants
7 x11.windows io.encodings.string io.encodings.ascii
8 io.encodings.utf8 combinators command-line qualified
9 math.vectors classes.tuple opengl.gl threads math.geometry.rect
10 environment ascii ;
11 IN: ui.x11
12
13 SINGLETON: x11-ui-backend
14
15 : XA_NET_WM_NAME ( -- atom ) "_NET_WM_NAME" x-atom ;
16
17 TUPLE: x11-handle window glx xic ;
18
19 C: <x11-handle> x11-handle
20
21 M: world expose-event nip relayout ;
22
23 M: world configure-event
24     over configured-loc >>window-loc
25     swap configured-dim >>dim
26     ! In case dimensions didn't change
27     relayout-1 ;
28
29 : modifiers
30     {
31         { S+ HEX: 1 }
32         { C+ HEX: 4 }
33         { A+ HEX: 8 }
34     } ;
35     
36 : key-codes
37     H{
38         { HEX: FF08 "BACKSPACE" }
39         { HEX: FF09 "TAB"       }
40         { HEX: FF0D "RET"       }
41         { HEX: FF8D "ENTER"     }
42         { HEX: FF1B "ESC"       }
43         { HEX: FFFF "DELETE"    }
44         { HEX: FF50 "HOME"      }
45         { HEX: FF51 "LEFT"      }
46         { HEX: FF52 "UP"        }
47         { HEX: FF53 "RIGHT"     }
48         { HEX: FF54 "DOWN"      }
49         { HEX: FF55 "PAGE_UP"   }
50         { HEX: FF56 "PAGE_DOWN" }
51         { HEX: FF57 "END"       }
52         { HEX: FF58 "BEGIN"     }
53         { HEX: FFBE "F1"        }
54         { HEX: FFBF "F2"        }
55         { HEX: FFC0 "F3"        }
56         { HEX: FFC1 "F4"        }
57         { HEX: FFC2 "F5"        }
58         { HEX: FFC3 "F6"        }
59         { HEX: FFC4 "F7"        }
60         { HEX: FFC5 "F8"        }
61         { HEX: FFC6 "F9"        }
62     } ;
63
64 : key-code ( keysym -- keycode action? )
65     dup key-codes at [ t ] [ 1string f ] ?if ;
66
67 : event-modifiers ( event -- seq )
68     XKeyEvent-state modifiers modifier ;
69
70 : valid-input? ( string gesture -- ? )
71     over empty? [ 2drop f ] [
72         mods>> { f { S+ } } member? [
73             [ [ 127 = not ] [ CHAR: \s >= ] bi and ] all?
74         ] [
75             [ [ 127 = not ] [ CHAR: \s >= ] [ alpha? not ] tri and and ] all?
76         ] if
77     ] if ;
78
79 : key-down-event>gesture ( event world -- string gesture )
80     dupd
81     handle>> xic>> lookup-string
82     [ swap event-modifiers ] dip key-code <key-down> ;
83
84 M: world key-down-event
85     [ key-down-event>gesture ] keep
86     world-focus
87     [ propagate-gesture drop ]
88     [ 2over valid-input? [ nip user-input ] [ 3drop ] if ]
89     3bi ;
90
91 : key-up-event>gesture ( event -- gesture )
92     dup event-modifiers swap 0 XLookupKeysym key-code <key-up> ;
93
94 M: world key-up-event
95     [ key-up-event>gesture ] dip world-focus propagate-gesture ;
96
97 : mouse-event>gesture ( event -- modifiers button loc )
98     [ event-modifiers ]
99     [ XButtonEvent-button ]
100     [ mouse-event-loc ]
101     tri ;
102
103 M: world button-down-event
104     [ mouse-event>gesture [ <button-down> ] dip ] dip
105     send-button-down ;
106
107 M: world button-up-event
108     [ mouse-event>gesture [ <button-up> ] dip ] dip
109     send-button-up ;
110
111 : mouse-event>scroll-direction ( event -- pair )
112     XButtonEvent-button {
113         { 4 { 0 -1 } }
114         { 5 { 0 1 } }
115         { 6 { -1 0 } }
116         { 7 { 1 0 } }
117     } at ;
118
119 M: world wheel-event
120     [ [ mouse-event>scroll-direction ] [ mouse-event-loc ] bi ] dip
121     send-wheel ;
122
123 M: world enter-event motion-event ;
124
125 M: world leave-event 2drop forget-rollover ;
126
127 M: world motion-event
128     [ [ XMotionEvent-x ] [ XMotionEvent-y ] bi 2array ] dip
129     move-hand fire-motion ;
130
131 M: world focus-in-event
132     nip
133     dup handle>> xic>> XSetICFocus focus-world ;
134
135 M: world focus-out-event
136     nip
137     dup handle>> xic>> XUnsetICFocus unfocus-world ;
138
139 M: world selection-notify-event
140     [ handle>> window>> selection-from-event ] keep
141     world-focus user-input ;
142
143 : supported-type? ( atom -- ? )
144     { "UTF8_STRING" "STRING" "TEXT" }
145     [ x-atom = ] with contains? ;
146
147 : clipboard-for-atom ( atom -- clipboard )
148     {
149         { XA_PRIMARY [ selection get ] }
150         { XA_CLIPBOARD [ clipboard get ] }
151         [ drop <clipboard> ]
152     } case ;
153
154 : encode-clipboard ( string type -- bytes )
155     XSelectionRequestEvent-target
156     XA_UTF8_STRING = utf8 ascii ? encode ;
157
158 : set-selection-prop ( evt -- )
159     dpy get swap
160     [ XSelectionRequestEvent-requestor ] keep
161     [ XSelectionRequestEvent-property ] keep
162     [ XSelectionRequestEvent-target ] keep
163     [ 8 PropModeReplace ] dip
164     [
165         XSelectionRequestEvent-selection
166         clipboard-for-atom contents>>
167     ] keep encode-clipboard dup length XChangeProperty drop ;
168
169 M: world selection-request-event
170     drop dup XSelectionRequestEvent-target {
171         { [ dup supported-type? ] [ drop dup set-selection-prop send-notify-success ] }
172         { [ dup "TARGETS" x-atom = ] [ drop dup set-targets-prop send-notify-success ] }
173         { [ dup "TIMESTAMP" x-atom = ] [ drop dup set-timestamp-prop send-notify-success ] }
174         [ drop send-notify-failure ]
175     } cond ;
176
177 M: x11-ui-backend (close-window) ( handle -- )
178     dup xic>> XDestroyIC
179     dup glx>> destroy-glx
180     window>> dup unregister-window
181     destroy-window ;
182
183 M: world client-event
184     swap close-box? [ ungraft ] [ drop ] if ;
185
186 : gadget-window ( world -- )
187     dup window-loc>> over rect-dim glx-window
188     over "Factor" create-xic <x11-handle>
189     2dup window>> register-window
190     >>handle drop ;
191
192 : wait-event ( -- event )
193     QueuedAfterFlush events-queued 0 > [
194         next-event dup
195         None XFilterEvent zero? [ drop wait-event ] unless
196     ] [
197         ui-wait wait-event
198     ] if ;
199
200 M: x11-ui-backend do-events
201     wait-event dup XAnyEvent-window window dup
202     [ handle-event ] [ 2drop ] if ;
203
204 : x-clipboard@ ( gadget clipboard -- prop win )
205     atom>> swap
206     find-world handle>> window>> ;
207
208 M: x-clipboard copy-clipboard
209     [ x-clipboard@ own-selection ] keep
210     (>>contents) ;
211
212 M: x-clipboard paste-clipboard
213     [ find-world handle>> window>> ] dip atom>> convert-selection ;
214
215 : init-clipboard ( -- )
216     XA_PRIMARY <x-clipboard> selection set-global
217     XA_CLIPBOARD <x-clipboard> clipboard set-global ;
218
219 : set-title-old ( dpy window string -- )
220     dup [ 127 <= ] all? [ XStoreName drop ] [ 3drop ] if ;
221
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 ;
225
226 M: x11-ui-backend set-title ( string world -- )
227     handle>> window>> swap
228     [ dpy get ] 2dip [ set-title-old ] [ set-title-new ] 3bi ;
229
230 M: x11-ui-backend set-fullscreen* ( ? world -- )
231     handle>> window>> "XClientMessageEvent" <c-object>
232     tuck set-XClientMessageEvent-window
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 ;
241
242 M: x11-ui-backend (open-window) ( world -- )
243     dup gadget-window
244     handle>> window>> dup set-closable map-window ;
245
246 M: x11-ui-backend raise-window* ( world -- )
247     handle>> [
248         dpy get swap window>> XRaiseWindow drop
249     ] when* ;
250
251 M: x11-handle select-gl-context ( handle -- )
252     dpy get swap
253     dup window>> swap glx>> glXMakeCurrent
254     [ "Failed to set current GLX context" throw ] unless ;
255
256 M: x11-handle flush-gl-context ( handle -- )
257     dpy get swap window>> glXSwapBuffers ;
258
259 M: x11-ui-backend ui ( -- )
260     [
261         f [
262             [
263                 stop-after-last-window? on
264                 init-clipboard
265                 start-ui
266                 event-loop
267             ] with-xim
268         ] with-x
269     ] ui-running ;
270
271 M: x11-ui-backend beep ( -- )
272     dpy get 100 XBell drop ;
273
274 x11-ui-backend ui-backend set-global
275
276 [ "DISPLAY" os-env "ui" "listener" ? ]
277 main-vocab-hook set-global