]> git.sesse.net Git - vlc/blob - modules/gui/skins2/x11/x11_loop.cpp
Copyright fixes
[vlc] / modules / gui / skins2 / x11 / x11_loop.cpp
1 /*****************************************************************************
2  * x11_loop.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 VideoLAN (Centrale Réseaux) and its contributors
5  * $Id$
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., 59 Temple Place - Suite 330, Boston, MA  02111, 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_keys.h"
46
47
48 // Maximum interval between clicks for a double-click (in microsec)
49 int X11Loop::m_dblClickDelay = 400000;
50
51
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 )
55 {
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 }
81
82
83 X11Loop::~X11Loop()
84 {
85 }
86
87
88 OSLoop *X11Loop::instance( intf_thread_t *pIntf, X11Display &rDisplay )
89 {
90     if( pIntf->p_sys->p_osLoop == NULL )
91     {
92         OSLoop *pOsLoop = new X11Loop( pIntf, rDisplay );
93         pIntf->p_sys->p_osLoop = pOsLoop;
94     }
95     return pIntf->p_sys->p_osLoop;
96 }
97
98
99 void X11Loop::destroy( intf_thread_t *pIntf )
100 {
101     if( pIntf->p_sys->p_osLoop )
102     {
103         delete pIntf->p_sys->p_osLoop;
104         pIntf->p_sys->p_osLoop = NULL;
105     }
106 }
107
108
109 void X11Loop::run()
110 {
111     OSFactory *pOsFactory = OSFactory::instance( getIntf() );
112     X11TimerLoop *pTimerLoop = ((X11Factory*)pOsFactory)->getTimerLoop();
113
114     // Main event loop
115     while( ! m_exit )
116     {
117         int nPending;
118
119         // Number of pending events in the queue
120         nPending = XPending( XDISPLAY );
121
122         while( ! m_exit && nPending > 0 )
123         {
124             // Handle the next X11 event
125             handleX11Event();
126
127             // Number of pending events in the queue
128             nPending = XPending( XDISPLAY );
129         }
130
131         // Wait for the next timer and execute it
132         // The sleep is interrupted if an X11 event is received
133         if( !m_exit )
134         {
135             pTimerLoop->waitNextTimer();
136         }
137     }
138 }
139
140
141 void X11Loop::exit()
142 {
143     m_exit = true;
144 }
145
146
147 void X11Loop::handleX11Event()
148 {
149     XEvent event;
150     OSFactory *pOsFactory = OSFactory::instance( getIntf() );
151
152     // Look for the next event in the queue
153     XNextEvent( XDISPLAY, &event );
154
155     if( event.xany.window == m_rDisplay.getMainWindow() )
156     {
157         if( event.type == MapNotify )
158         {
159             // When the "parent" window is mapped, show all the visible
160             // windows, as it is not automatic, unfortunately
161             Theme *pTheme = getIntf()->p_sys->p_theme;
162             if( pTheme )
163             {
164                 pTheme->getWindowManager().synchVisibility();
165             }
166         }
167         return;
168     }
169
170     // Find the window to which the event is sent
171     GenericWindow *pWin =
172         ((X11Factory*)pOsFactory)->m_windowMap[event.xany.window];
173
174     if( !pWin )
175     {
176         msg_Dbg( getIntf(), "No associated generic window !!" );
177         return;
178     }
179
180     // Send the right event object to the window
181     switch( event.type )
182     {
183         case Expose:
184         {
185             EvtRefresh evt( getIntf(), event.xexpose.x,
186                             event.xexpose.y, event.xexpose.width,
187                             event.xexpose.height );
188             pWin->processEvent( evt );
189             break;
190         }
191         case FocusIn:
192         {
193             EvtFocus evt( getIntf(), true );
194             pWin->processEvent( evt );
195             break;
196         }
197         case FocusOut:
198         {
199             EvtFocus evt( getIntf(), false );
200             pWin->processEvent( evt );
201             break;
202         }
203
204         case MotionNotify:
205         {
206             // Don't trust the position in the event, it is
207             // out of date. Get the actual current position instead
208             int x, y;
209             pOsFactory->getMousePos( x, y );
210             EvtMotion evt( getIntf(), x, y );
211             pWin->processEvent( evt );
212             break;
213         }
214         case LeaveNotify:
215         {
216             EvtLeave evt( getIntf() );
217             pWin->processEvent( evt );
218             break;
219         }
220         case ButtonPress:
221         case ButtonRelease:
222         {
223             EvtMouse::ActionType_t action = EvtMouse::kDown;
224             switch( event.type )
225             {
226                 case ButtonPress:
227                     action = EvtMouse::kDown;
228                     break;
229                 case ButtonRelease:
230                     action = EvtMouse::kUp;
231                     break;
232             }
233
234             // Get the modifiers
235             int mod = EvtInput::kModNone;
236             if( event.xbutton.state & Mod1Mask )
237             {
238                 mod |= EvtInput::kModAlt;
239             }
240             if( event.xbutton.state & ControlMask )
241             {
242                 mod |= EvtInput::kModCtrl;
243             }
244             if( event.xbutton.state & ShiftMask )
245             {
246                 mod |= EvtInput::kModShift;
247             }
248
249             // Check for double clicks
250             if( event.type == ButtonPress &&
251                 event.xbutton.button == 1 )
252             {
253                 mtime_t time = mdate();
254                 int x, y;
255                 pOsFactory->getMousePos( x, y );
256                 if( time - m_lastClickTime < m_dblClickDelay &&
257                     x == m_lastClickPosX && y == m_lastClickPosY )
258                 {
259                     m_lastClickTime = 0;
260                     action = EvtMouse::kDblClick;
261                 }
262                 else
263                 {
264                     m_lastClickTime = time;
265                     m_lastClickPosX = x;
266                     m_lastClickPosY = y;
267                 }
268             }
269
270             switch( event.xbutton.button )
271             {
272                 case 1:
273                 {
274                     EvtMouse evt( getIntf(), event.xbutton.x,
275                                   event.xbutton.y, EvtMouse::kLeft,
276                                   action, mod );
277                     pWin->processEvent( evt );
278                     break;
279                 }
280                 case 2:
281                 {
282                     EvtMouse evt( getIntf(), event.xbutton.x,
283                                   event.xbutton.y, EvtMouse::kMiddle,
284                                   action, mod );
285                     pWin->processEvent( evt );
286                     break;
287                 }
288                 case 3:
289                 {
290                     EvtMouse evt( getIntf(), event.xbutton.x,
291                                   event.xbutton.y, EvtMouse::kRight,
292                                   action, mod );
293                     pWin->processEvent( evt );
294                     break;
295                 }
296                 case 4:
297                 {
298                     // Scroll up
299                     EvtScroll evt( getIntf(), event.xbutton.x,
300                                    event.xbutton.y, EvtScroll::kUp,
301                                    mod );
302                     pWin->processEvent( evt );
303                     break;
304                 }
305                 case 5:
306                 {
307                     // Scroll down
308                     EvtScroll evt( getIntf(), event.xbutton.x,
309                                    event.xbutton.y, EvtScroll::kDown,
310                                    mod );
311                     pWin->processEvent( evt );
312                     break;
313                 }
314             }
315             break;
316         }
317         case KeyPress:
318         case KeyRelease:
319         {
320             EvtKey::ActionType_t action = EvtKey::kDown;
321             int mod = EvtInput::kModNone;
322             // Get the modifiers
323             if( event.xkey.state & Mod1Mask )
324             {
325                 mod |= EvtInput::kModAlt;
326             }
327             if( event.xkey.state & ControlMask )
328             {
329                 mod |= EvtInput::kModCtrl;
330             }
331             if( event.xkey.state & ShiftMask )
332             {
333                 mod |= EvtInput::kModShift;
334             }
335
336             // Take the first keysym = lower case character
337             KeySym keysym = XLookupKeysym( &event.xkey, 0 );
338
339             // Get VLC key code from the keysym
340             int key = keysymToVlcKey[keysym];
341             if( !key )
342             {
343                 // Normal key
344                 key = keysym;
345             }
346
347             switch( event.type )
348             {
349                 case KeyPress:
350                     action = EvtKey::kDown;
351                     break;
352                 case KeyRelease:
353                     action = EvtKey::kUp;
354                     break;
355             }
356             EvtKey evt( getIntf(), key, action, mod );
357             pWin->processEvent( evt );
358             break;
359         }
360
361         case ClientMessage:
362         {
363             // Get the message type
364             string type = XGetAtomName( XDISPLAY, event.xclient.message_type );
365
366             // Find the DnD object for this window
367             X11DragDrop *pDnd =
368                 ((X11Factory*)pOsFactory)->m_dndMap[event.xany.window];
369             if( !pDnd )
370             {
371                 msg_Err( getIntf(), "No associated D&D object !!" );
372                 return;
373             }
374
375             if( type == "XdndEnter" )
376             {
377                 pDnd->dndEnter( event.xclient.data.l );
378             }
379             else if( type == "XdndPosition" )
380             {
381                 pDnd->dndPosition( event.xclient.data.l );
382             }
383             else if( type == "XdndLeave" )
384             {
385                 pDnd->dndLeave( event.xclient.data.l );
386             }
387             else if( type == "XdndDrop" )
388             {
389                 pDnd->dndDrop( event.xclient.data.l );
390             }
391             break;
392         }
393     }
394 }
395
396 #endif