1 /*****************************************************************************
2 * x11_loop.cpp
3 *****************************************************************************
4 * Copyright (C) 2003 the VideoLAN team
5 * $Id: 0aa682e0a32a57fce5429a0ebd5ea01fb5f9ba36 $
6 *
7 * Authors: Cyril Deguet <asmax@via.ecp.fr>
8 * Olivier Teulière <ipkiss@via.ecp.fr>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
24
25 #ifdef X11_SKINS
26
27 #include <X11/keysym.h>
28 #include "x11_loop.hpp"
29 #include "x11_display.hpp"
30 #include "x11_dragdrop.hpp"
31 #include "x11_factory.hpp"
32 #include "x11_timer.hpp"
33 #include "../src/generic_window.hpp"
34 #include "../src/theme.hpp"
35 #include "../src/window_manager.hpp"
36 #include "../events/evt_focus.hpp"
37 #include "../events/evt_key.hpp"
38 #include "../events/evt_mouse.hpp"
39 #include "../events/evt_motion.hpp"
40 #include "../events/evt_leave.hpp"
41 #include "../events/evt_refresh.hpp"
42 #include "../events/evt_scroll.hpp"
43 #include "../commands/async_queue.hpp"
44 #include "../utils/var_bool.hpp"
45 #include <vlc_actions.h>
46
47
48 // Maximum interval between clicks for a double-click (in microsec)
49 int X11Loop::m_dblClickDelay = 400000;
50
51 X11Loop::keymap_t X11Loop::m_keymap;
52
X11Loop(intf_thread_t * pIntf,X11Display & rDisplay)53 X11Loop::X11Loop( intf_thread_t *pIntf, X11Display &rDisplay ):
54 OSLoop( pIntf ), m_rDisplay( rDisplay ), m_exit( false ),
55 m_lastClickTime( 0 ), m_lastClickPosX( 0 ), m_lastClickPosY( 0 )
56 {
57 if(m_keymap.empty()) {
58 // Initialize the key map where VLC keys differ from X11 keys.
59 m_keymap[XK_F1] = KEY_F1;
60 m_keymap[XK_F2] = KEY_F2;
61 m_keymap[XK_F3] = KEY_F3;
62 m_keymap[XK_F4] = KEY_F4;
63 m_keymap[XK_F5] = KEY_F5;
64 m_keymap[XK_F6] = KEY_F6;
65 m_keymap[XK_F7] = KEY_F7;
66 m_keymap[XK_F8] = KEY_F8;
67 m_keymap[XK_F9] = KEY_F9;
68 m_keymap[XK_F10] = KEY_F10;
69 m_keymap[XK_F11] = KEY_F11;
70 m_keymap[XK_F12] = KEY_F12;
71 m_keymap[XK_Return] = KEY_ENTER;
72 m_keymap[XK_Escape] = KEY_ESC;
73 m_keymap[XK_Left] = KEY_LEFT;
74 m_keymap[XK_Right] = KEY_RIGHT;
75 m_keymap[XK_Up] = KEY_UP;
76 m_keymap[XK_Down] = KEY_DOWN;
77 m_keymap[XK_Home] = KEY_HOME;
78 m_keymap[XK_End] = KEY_END;
79 m_keymap[XK_Prior] = KEY_PAGEUP;
80 m_keymap[XK_Next] = KEY_PAGEDOWN;
81 m_keymap[XK_Delete] = KEY_DELETE;
82 m_keymap[XK_Insert] = KEY_INSERT;
83 }
84 }
85
86
~X11Loop()87 X11Loop::~X11Loop()
88 {
89 }
90
91
instance(intf_thread_t * pIntf,X11Display & rDisplay)92 OSLoop *X11Loop::instance( intf_thread_t *pIntf, X11Display &rDisplay )
93 {
94 if( pIntf->p_sys->p_osLoop == NULL )
95 {
96 OSLoop *pOsLoop = new X11Loop( pIntf, rDisplay );
97 pIntf->p_sys->p_osLoop = pOsLoop;
98 }
99 return pIntf->p_sys->p_osLoop;
100 }
101
102
destroy(intf_thread_t * pIntf)103 void X11Loop::destroy( intf_thread_t *pIntf )
104 {
105 delete pIntf->p_sys->p_osLoop;
106 pIntf->p_sys->p_osLoop = NULL;
107 }
108
109
run()110 void X11Loop::run()
111 {
112 OSFactory *pOsFactory = OSFactory::instance( getIntf() );
113 X11TimerLoop *pTimerLoop = ((X11Factory*)pOsFactory)->getTimerLoop();
114
115 // Main event loop
116 while( ! m_exit )
117 {
118 int nPending;
119
120 // Number of pending events in the queue
121 nPending = XPending( XDISPLAY );
122
123 while( ! m_exit && nPending > 0 )
124 {
125 // Handle the next X11 event
126 handleX11Event();
127
128 // Number of pending events in the queue
129 nPending = XPending( XDISPLAY );
130 }
131
132 // Wait for the next timer and execute it
133 // The sleep is interrupted if an X11 event is received
134 if( !m_exit )
135 {
136 pTimerLoop->waitNextTimer();
137 }
138 }
139 }
140
141
exit()142 void X11Loop::exit()
143 {
144 m_exit = true;
145 }
146
147
X11ModToMod(unsigned state)148 inline int X11Loop::X11ModToMod( unsigned state )
149 {
150 int mod = EvtInput::kModNone;
151 if( state & Mod1Mask )
152 mod |= EvtInput::kModAlt;
153 if( state & ControlMask )
154 mod |= EvtInput::kModCtrl;
155 if( state & ShiftMask )
156 mod |= EvtInput::kModShift;
157 return mod;
158 }
159
160
handleX11Event()161 void X11Loop::handleX11Event()
162 {
163 XEvent event;
164 OSFactory *pOsFactory = OSFactory::instance( getIntf() );
165
166 // Look for the next event in the queue
167 XNextEvent( XDISPLAY, &event );
168
169 if( event.xany.window == m_rDisplay.getMainWindow() )
170 {
171 if( event.type == ClientMessage )
172 {
173 Atom wm_protocols =
174 XInternAtom( XDISPLAY, "WM_PROTOCOLS", False);
175 Atom wm_delete =
176 XInternAtom( XDISPLAY, "WM_DELETE_WINDOW", False);
177
178 if( event.xclient.message_type == wm_protocols &&
179 (Atom)event.xclient.data.l[0] == wm_delete )
180 {
181 msg_Dbg( getIntf(), "Received WM_DELETE_WINDOW message" );
182 libvlc_Quit( getIntf()->obj.libvlc );
183 }
184 }
185 return;
186 }
187
188 // Find the window to which the event is sent
189 GenericWindow *pWin =
190 ((X11Factory*)pOsFactory)->m_windowMap[event.xany.window];
191
192 if( !pWin )
193 {
194 return;
195 }
196
197 // Send the right event object to the window
198 switch( event.type )
199 {
200 case Expose:
201 {
202 EvtRefresh evt( getIntf(), event.xexpose.x,
203 event.xexpose.y, event.xexpose.width,
204 event.xexpose.height );
205 pWin->processEvent( evt );
206 break;
207 }
208 case FocusIn:
209 {
210 EvtFocus evt( getIntf(), true );
211 pWin->processEvent( evt );
212 break;
213 }
214 case FocusOut:
215 {
216 EvtFocus evt( getIntf(), false );
217 pWin->processEvent( evt );
218 break;
219 }
220
221 case MotionNotify:
222 {
223 // Don't trust the position in the event, it is
224 // out of date. Get the actual current position instead
225 int x, y;
226 pOsFactory->getMousePos( x, y );
227 EvtMotion evt( getIntf(), x, y );
228 pWin->processEvent( evt );
229 break;
230 }
231 case LeaveNotify:
232 {
233 EvtLeave evt( getIntf() );
234 pWin->processEvent( evt );
235 break;
236 }
237 case ButtonPress:
238 case ButtonRelease:
239 {
240 EvtMouse::ActionType_t action = EvtMouse::kDown;
241 switch( event.type )
242 {
243 case ButtonPress:
244 action = EvtMouse::kDown;
245 break;
246 case ButtonRelease:
247 action = EvtMouse::kUp;
248 break;
249 }
250
251 int mod = X11ModToMod( event.xbutton.state );
252
253 // Check for double clicks
254 if( event.type == ButtonPress &&
255 event.xbutton.button == 1 )
256 {
257 mtime_t time = mdate();
258 int x, y;
259 pOsFactory->getMousePos( x, y );
260 if( time - m_lastClickTime < m_dblClickDelay &&
261 x == m_lastClickPosX && y == m_lastClickPosY )
262 {
263 m_lastClickTime = 0;
264 action = EvtMouse::kDblClick;
265 }
266 else
267 {
268 m_lastClickTime = time;
269 m_lastClickPosX = x;
270 m_lastClickPosY = y;
271 }
272 }
273
274 switch( event.xbutton.button )
275 {
276 case 1:
277 {
278 EvtMouse evt( getIntf(), event.xbutton.x,
279 event.xbutton.y, EvtMouse::kLeft,
280 action, mod );
281 pWin->processEvent( evt );
282 break;
283 }
284 case 2:
285 {
286 EvtMouse evt( getIntf(), event.xbutton.x,
287 event.xbutton.y, EvtMouse::kMiddle,
288 action, mod );
289 pWin->processEvent( evt );
290 break;
291 }
292 case 3:
293 {
294 EvtMouse evt( getIntf(), event.xbutton.x,
295 event.xbutton.y, EvtMouse::kRight,
296 action, mod );
297 pWin->processEvent( evt );
298 break;
299 }
300 case 4:
301 {
302 // Scroll up
303 if( event.type == ButtonPress )
304 {
305 EvtScroll evt( getIntf(), event.xbutton.x,
306 event.xbutton.y, EvtScroll::kUp,
307 mod );
308 pWin->processEvent( evt );
309 }
310 break;
311 }
312 case 5:
313 {
314 // Scroll down
315 if( event.type == ButtonPress )
316 {
317 EvtScroll evt( getIntf(), event.xbutton.x,
318 event.xbutton.y, EvtScroll::kDown,
319 mod );
320 pWin->processEvent( evt );
321 }
322 break;
323 }
324 }
325 break;
326 }
327 case KeyPress:
328 case KeyRelease:
329 {
330 // Take the first keysym = lower case character, and translate.
331 int key = keysymToVlcKey( XLookupKeysym( &event.xkey, 0 ) );
332
333 EvtKey evt( getIntf(), key,
334 event.type==KeyRelease ? EvtKey::kUp : EvtKey::kDown,
335 X11ModToMod( event.xkey.state ) );
336 pWin->processEvent( evt );
337 break;
338 }
339
340 case ClientMessage:
341 {
342 // Get the message type
343 std::string type = XGetAtomName( XDISPLAY, event.xclient.message_type );
344
345 // Find the DnD object for this window
346 X11DragDrop *pDnd =
347 ((X11Factory*)pOsFactory)->m_dndMap[event.xany.window];
348 if( !pDnd )
349 {
350 msg_Err( getIntf(), "no associated D&D object" );
351 return;
352 }
353
354 if( type == "XdndEnter" )
355 pDnd->dndEnter( event.xclient.data.l );
356 else if( type == "XdndPosition" )
357 pDnd->dndPosition( event.xclient.data.l );
358 else if( type == "XdndLeave" )
359 pDnd->dndLeave( event.xclient.data.l );
360 else if( type == "XdndDrop" )
361 pDnd->dndDrop( event.xclient.data.l );
362 break;
363 }
364 }
365 }
366
367 #endif
368