1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2003 the VideoLAN team
7 * Authors: Cyril Deguet <asmax@via.ecp.fr>
8 * Olivier Teulière <ipkiss@via.ecp.fr>
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.
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.
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 *****************************************************************************/
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"
48 // Maximum interval between clicks for a double-click (in microsec)
49 int X11Loop::m_dblClickDelay = 400000;
52 X11Loop::X11Loop( intf_thread_t *pIntf, X11Display &rDisplay ):
53 OSLoop( pIntf ), m_rDisplay( rDisplay ), m_exit( false ),
54 m_lastClickTime( 0 ), m_lastClickPosX( 0 ), m_lastClickPosY( 0 )
56 // Initialize the key map
57 keysymToVlcKey[XK_F1] = KEY_F1;
58 keysymToVlcKey[XK_F2] = KEY_F2;
59 keysymToVlcKey[XK_F3] = KEY_F3;
60 keysymToVlcKey[XK_F4] = KEY_F4;
61 keysymToVlcKey[XK_F5] = KEY_F5;
62 keysymToVlcKey[XK_F6] = KEY_F6;
63 keysymToVlcKey[XK_F7] = KEY_F7;
64 keysymToVlcKey[XK_F8] = KEY_F8;
65 keysymToVlcKey[XK_F9] = KEY_F9;
66 keysymToVlcKey[XK_F10] = KEY_F10;
67 keysymToVlcKey[XK_F11] = KEY_F11;
68 keysymToVlcKey[XK_F12] = KEY_F12;
69 keysymToVlcKey[XK_Return] = KEY_ENTER;
70 keysymToVlcKey[XK_space] = KEY_SPACE;
71 keysymToVlcKey[XK_Escape] = KEY_ESC;
72 keysymToVlcKey[XK_Left] = KEY_LEFT;
73 keysymToVlcKey[XK_Right] = KEY_RIGHT;
74 keysymToVlcKey[XK_Up] = KEY_UP;
75 keysymToVlcKey[XK_Down] = KEY_DOWN;
76 keysymToVlcKey[XK_Home] = KEY_HOME;
77 keysymToVlcKey[XK_End] = KEY_END;
78 keysymToVlcKey[XK_Prior] = KEY_PAGEUP;
79 keysymToVlcKey[XK_Next] = KEY_PAGEDOWN;
80 keysymToVlcKey[XK_Delete] = KEY_DELETE;
81 keysymToVlcKey[XK_Insert] = KEY_INSERT;
90 OSLoop *X11Loop::instance( intf_thread_t *pIntf, X11Display &rDisplay )
92 if( pIntf->p_sys->p_osLoop == NULL )
94 OSLoop *pOsLoop = new X11Loop( pIntf, rDisplay );
95 pIntf->p_sys->p_osLoop = pOsLoop;
97 return pIntf->p_sys->p_osLoop;
101 void X11Loop::destroy( intf_thread_t *pIntf )
103 if( pIntf->p_sys->p_osLoop )
105 delete pIntf->p_sys->p_osLoop;
106 pIntf->p_sys->p_osLoop = NULL;
113 OSFactory *pOsFactory = OSFactory::instance( getIntf() );
114 X11TimerLoop *pTimerLoop = ((X11Factory*)pOsFactory)->getTimerLoop();
121 // Number of pending events in the queue
122 nPending = XPending( XDISPLAY );
124 while( ! m_exit && nPending > 0 )
126 // Handle the next X11 event
129 // Number of pending events in the queue
130 nPending = XPending( XDISPLAY );
133 // Wait for the next timer and execute it
134 // The sleep is interrupted if an X11 event is received
137 pTimerLoop->waitNextTimer();
149 void X11Loop::handleX11Event()
152 OSFactory *pOsFactory = OSFactory::instance( getIntf() );
154 // Look for the next event in the queue
155 XNextEvent( XDISPLAY, &event );
157 if( event.xany.window == m_rDisplay.getMainWindow() )
159 if( event.type == MapNotify )
161 // When the "parent" window is mapped, show all the visible
162 // windows, as it is not automatic, unfortunately
163 Theme *pTheme = getIntf()->p_sys->p_theme;
166 pTheme->getWindowManager().synchVisibility();
172 // Find the window to which the event is sent
173 GenericWindow *pWin =
174 ((X11Factory*)pOsFactory)->m_windowMap[event.xany.window];
181 // Send the right event object to the window
186 EvtRefresh evt( getIntf(), event.xexpose.x,
187 event.xexpose.y, event.xexpose.width,
188 event.xexpose.height );
189 pWin->processEvent( evt );
194 EvtFocus evt( getIntf(), true );
195 pWin->processEvent( evt );
200 EvtFocus evt( getIntf(), false );
201 pWin->processEvent( evt );
207 // Don't trust the position in the event, it is
208 // out of date. Get the actual current position instead
210 pOsFactory->getMousePos( x, y );
211 EvtMotion evt( getIntf(), x, y );
212 pWin->processEvent( evt );
217 EvtLeave evt( getIntf() );
218 pWin->processEvent( evt );
224 EvtMouse::ActionType_t action = EvtMouse::kDown;
228 action = EvtMouse::kDown;
231 action = EvtMouse::kUp;
236 int mod = EvtInput::kModNone;
237 if( event.xbutton.state & Mod1Mask )
239 mod |= EvtInput::kModAlt;
241 if( event.xbutton.state & ControlMask )
243 mod |= EvtInput::kModCtrl;
245 if( event.xbutton.state & ShiftMask )
247 mod |= EvtInput::kModShift;
250 // Check for double clicks
251 if( event.type == ButtonPress &&
252 event.xbutton.button == 1 )
254 mtime_t time = mdate();
256 pOsFactory->getMousePos( x, y );
257 if( time - m_lastClickTime < m_dblClickDelay &&
258 x == m_lastClickPosX && y == m_lastClickPosY )
261 action = EvtMouse::kDblClick;
265 m_lastClickTime = time;
271 switch( event.xbutton.button )
275 EvtMouse evt( getIntf(), event.xbutton.x,
276 event.xbutton.y, EvtMouse::kLeft,
278 pWin->processEvent( evt );
283 EvtMouse evt( getIntf(), event.xbutton.x,
284 event.xbutton.y, EvtMouse::kMiddle,
286 pWin->processEvent( evt );
291 EvtMouse evt( getIntf(), event.xbutton.x,
292 event.xbutton.y, EvtMouse::kRight,
294 pWin->processEvent( evt );
300 EvtScroll evt( getIntf(), event.xbutton.x,
301 event.xbutton.y, EvtScroll::kUp,
303 pWin->processEvent( evt );
309 EvtScroll evt( getIntf(), event.xbutton.x,
310 event.xbutton.y, EvtScroll::kDown,
312 pWin->processEvent( evt );
321 EvtKey::ActionType_t action = EvtKey::kDown;
322 int mod = EvtInput::kModNone;
324 if( event.xkey.state & Mod1Mask )
326 mod |= EvtInput::kModAlt;
328 if( event.xkey.state & ControlMask )
330 mod |= EvtInput::kModCtrl;
332 if( event.xkey.state & ShiftMask )
334 mod |= EvtInput::kModShift;
337 // Take the first keysym = lower case character
338 KeySym keysym = XLookupKeysym( &event.xkey, 0 );
340 // Get VLC key code from the keysym
341 int key = keysymToVlcKey[keysym];
351 action = EvtKey::kDown;
354 action = EvtKey::kUp;
357 EvtKey evt( getIntf(), key, action, mod );
358 pWin->processEvent( evt );
364 // Get the message type
365 string type = XGetAtomName( XDISPLAY, event.xclient.message_type );
367 // Find the DnD object for this window
369 ((X11Factory*)pOsFactory)->m_dndMap[event.xany.window];
372 msg_Err( getIntf(), "no associated D&D object" );
376 if( type == "XdndEnter" )
378 pDnd->dndEnter( event.xclient.data.l );
380 else if( type == "XdndPosition" )
382 pDnd->dndPosition( event.xclient.data.l );
384 else if( type == "XdndLeave" )
386 pDnd->dndLeave( event.xclient.data.l );
388 else if( type == "XdndDrop" )
390 pDnd->dndDrop( event.xclient.data.l );