]> git.sesse.net Git - vlc/blob - modules/gui/skins/x11/x11_window.cpp
* modules/gui/skins/*: Added a "playondrop" attribute to the "Window"
[vlc] / modules / gui / skins / x11 / x11_window.cpp
1 /*****************************************************************************
2  * x11_window.cpp: X11 implementation of the Window class
3  *****************************************************************************
4  * Copyright (C) 2003 VideoLAN
5  * $Id: x11_window.cpp,v 1.30 2003/10/22 19:12:56 ipkiss Exp $
6  *
7  * Authors: Cyril Deguet     <asmax@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111,
22  * USA.
23  *****************************************************************************/
24
25 #ifdef X11_SKINS
26
27 //--- GENERAL ---------------------------------------------------------------
28 //#include <math.h>
29
30 //--- VLC -------------------------------------------------------------------
31 #include <vlc/intf.h>
32
33 //--- X11 -------------------------------------------------------------------
34 #include <X11/Xlib.h>
35 #include <X11/Xatom.h>
36 #include <X11/extensions/shape.h>
37
38 //--- SKIN ------------------------------------------------------------------
39 #include "../os_api.h"
40 #include "../src/anchor.h"
41 #include "../controls/generic.h"
42 #include "../src/window.h"
43 #include "../os_window.h"
44 #include "../src/event.h"
45 #include "../os_event.h"
46 #include "../src/graphics.h"
47 #include "../os_graphics.h"
48 #include "../src/skin_common.h"
49 #include "../src/theme.h"
50 #include "../os_theme.h"
51 #include "x11_timer.h"
52
53
54 static bool ToolTipCallback( void *data );
55 static void DrawToolTipText( tooltip_t *tooltip );
56
57
58 //---------------------------------------------------------------------------
59 // Skinable Window
60 //---------------------------------------------------------------------------
61 X11Window::X11Window( intf_thread_t *p_intf, Window wnd, int x, int y,
62     bool visible, int transition, int normalalpha, int movealpha,
63     bool dragdrop, bool playondrop, string name )
64     : SkinWindow( p_intf, x, y, visible, transition, normalalpha, movealpha,
65               dragdrop  )
66 {
67     // Set handles
68     Wnd         = wnd;
69     display     = p_intf->p_sys->display;
70     int screen  = DefaultScreen( display );
71     Name        = name;
72     LButtonDown = false;
73     RButtonDown = false;
74
75     // Creation of a graphic context that doesn't generate a GraphicsExpose
76     // event when using functions like XCopyArea
77     XGCValues gcVal;
78     gcVal.graphics_exposures = False;
79     XLOCK;
80     Gc = XCreateGC( display, wnd, GCGraphicsExposures, &gcVal );
81     XUNLOCK;
82
83     // Removing fading effect
84     Transition  = 0;
85
86     if( DragDrop )
87     {
88         // register the listview as a drop target
89         DropObject = new X11DropObject( p_intf, Wnd, playondrop );
90
91         Atom xdndAtom = XInternAtom( display, "XdndAware", False );
92         char xdndVersion = 4;
93         XLOCK;
94         XChangeProperty( display, wnd, xdndAtom, XA_ATOM, 32,
95                          PropModeReplace, (unsigned char *)&xdndVersion, 1);
96         XUNLOCK;
97     }
98
99     // Associate vlc icon to the window
100     XLOCK;
101     XWMHints *hints = XGetWMHints( display, Wnd );
102     if( !hints)
103     {
104         hints = XAllocWMHints();
105     }
106     if( p_intf->p_sys->iconPixmap != None )
107     {
108         hints->icon_pixmap = p_intf->p_sys->iconPixmap;
109         hints->flags |= IconPixmapHint;
110     }
111     if( p_intf->p_sys->iconMask != None )
112     {
113         hints->icon_mask = p_intf->p_sys->iconMask;
114         hints->flags |= IconMaskHint;
115     }
116     XSetWMHints( display, Wnd, hints );
117     XFree( hints );
118     XUNLOCK;
119
120     // Create Tool Tip window
121     XColor color;
122     color.red = 0xffff;
123     color.green = 0xffff;
124     color.blue = 0xa000;
125     Colormap cm = DefaultColormap( display, screen );
126     Window root = DefaultRootWindow( display );
127
128     XLOCK;
129     XAllocColor( display, cm, &color );
130     XSetWindowAttributes attr;
131     attr.background_pixel = color.pixel;
132     attr.override_redirect = True;
133     ToolTip.window = XCreateWindow( display, root, 0, 0, 1, 1, 1, 0,
134                                     InputOutput, CopyFromParent,
135                                     CWBackPixel|CWOverrideRedirect, &attr );
136     ToolTip.font = XLoadFont( display,
137                               "-*-helvetica-bold-r-*-*-*-80-*-*-*-*-*-*" );
138     gcVal.font = ToolTip.font;
139     gcVal.foreground = 0;
140     gcVal.background = color.pixel;
141     ToolTip.gc = XCreateGC( display, ToolTip.window, 
142                             GCBackground|GCForeground|GCFont, &gcVal );
143     XUNLOCK;
144
145     ToolTip.display = display;
146     X11Timer *timer = new X11Timer( p_intf, 500000, ToolTipCallback, &ToolTip );
147     ToolTip.p_intf = p_intf;
148     ToolTip.timer = timer;
149     ToolTip.active = False;
150
151     // Double-click handling
152     ClickedX = 0;
153     ClickedY = 0;
154     ClickedTime = 0;
155     // TODO: can be retrieved somewhere ?
156     DblClickDelay = 400;
157 }
158 //---------------------------------------------------------------------------
159 X11Window::~X11Window()
160 {
161     if( DragDrop )
162     {
163         delete DropObject;
164     }
165     delete ToolTip.timer;
166     XLOCK;
167     XFreeGC( display, ToolTip.gc );
168     XFreeGC( display, Gc );
169     XDestroyWindow( display, ToolTip.window );
170     XDestroyWindow( display, Wnd );
171     XUNLOCK;
172 }
173 //---------------------------------------------------------------------------
174 void X11Window::ToggleOnTop()
175 {
176     XEvent ev;
177     ev.type = ClientMessage;
178     ev.xclient.display = display;
179     ev.xclient.window = Wnd;
180     ev.xclient.serial = 0;
181     ev.xclient.send_event = True;
182     ev.xclient.message_type = XInternAtom( display, "_NET_WM_STATE", True );
183     ev.xclient.format = 32;
184     ev.xclient.data.l[1] = XInternAtom( display, "_NET_WM_STATE_ABOVE", False);
185     ev.xclient.data.l[2] = 0;
186     ev.xclient.data.l[3] = 0;
187     ev.xclient.data.l[4] = 0;
188
189     if( !p_intf->p_sys->b_on_top )
190     {
191         // Set the window on top
192         ev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
193     }
194     else
195     {
196         // Set the window not on top
197         ev.xclient.data.l[0] = 0; // _NET_WM_STATE_REMOVE
198     }
199
200     XLOCK;
201     XSendEvent( display, DefaultRootWindow( display ), False,
202                 SubstructureRedirectMask | SubstructureNotifyMask, &ev );
203     XUNLOCK;
204
205     /* For KDE */
206     ev.xclient.data.l[1] = XInternAtom( display, "_NET_WM_STATE_STAYS_ON_TOP",
207                                         False);
208     XLOCK;
209     XSendEvent( display, DefaultRootWindow( display ), False,
210                 SubstructureRedirectMask | SubstructureNotifyMask, &ev );
211     XUNLOCK;
212 }
213 //---------------------------------------------------------------------------
214 void X11Window::OSShow( bool show )
215 {
216     XLOCK;
217     XResizeWindow( display, Wnd, 1, 1 ); // Avoid flicker
218     XUNLOCK;
219
220     if( show )
221     {
222         // We do the call to XShapeCombineRegion() here because the window
223         // must be unmapped for this to work.
224         Drawable drawable = (( X11Graphics* )Image )->GetImage();
225
226         XLOCK;
227         XImage *image = XGetImage( display, drawable, 0, 0, Width, Height, 
228                                    AllPlanes, ZPixmap );
229         if( image )
230         {
231             // Mask for transparency
232             Region region = XCreateRegion();
233             for( int line = 0; line < Height; line++ )
234             {
235                 int start = 0, end = 0;
236                 while( start < Width )
237                 {
238                     while( start < Width && XGetPixel( image, start, line )
239                            == 0 )
240                     {
241                         start++;
242                     }
243                     end = start;
244                     while( end < Width && XGetPixel( image, end, line ) != 0)
245                     {
246                         end++;
247                     }
248                     XRectangle rect;
249                     rect.x = start;
250                     rect.y = line;
251                     rect.width = end - start + 1;
252                     rect.height = 1;
253                     Region newRegion = XCreateRegion();
254                     XUnionRectWithRegion( &rect, region, newRegion );
255                     XDestroyRegion( region );
256                     region = newRegion;
257                     start = end + 1;
258                 }
259             }
260             XDestroyImage( image );
261
262             XShapeCombineRegion( display, Wnd, ShapeBounding, 0, 0, region,
263                                  ShapeSet );
264             XDestroyRegion( region );
265
266         }
267         else
268         {
269             msg_Err( p_intf, "X11Window::OSShow XShapeCombineRegion() failed");
270         }
271
272         XMapWindow( display, Wnd );
273         XMoveResizeWindow( display, Wnd, Left, Top, Width, Height );
274         XUNLOCK;
275     }
276     else
277     {
278         XLOCK;
279         XUnmapWindow( display, Wnd );
280         XUNLOCK;
281     }
282 }
283 //---------------------------------------------------------------------------
284 bool X11Window::ProcessOSEvent( Event *evt )
285 {
286     unsigned int msg = evt->GetMessage();
287     //unsigned int p1  = evt->GetParam1();
288     int          p2  = evt->GetParam2();
289     int          time;
290     int          posX, posY;
291     string       type;
292     int          button;
293
294     switch( msg )
295     {
296         case Expose:
297             RefreshFromImage( 0, 0, Width, Height );
298             return true;
299
300         case MotionNotify:
301             if( LButtonDown )
302                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
303                            (int)( (XMotionEvent *)p2 )->y, 1 );
304             else if( RButtonDown )
305                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
306                            (int)( (XMotionEvent *)p2 )->y, 2 );
307             else
308                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
309                            (int)( (XMotionEvent *)p2 )->y, 0 );
310             return true;
311
312
313         case ButtonPress:
314             // Raise all the windows
315             for( list<SkinWindow *>::const_iterator win = 
316                     p_intf->p_sys->p_theme->WindowList.begin();
317                     win != p_intf->p_sys->p_theme->WindowList.end(); win++ )
318             {
319                 Window id = ( (X11Window *)(*win) )->GetHandle();
320                 // the current window is raised last
321                 if( id != Wnd )
322                 {
323                     XLOCK;
324                     XRaiseWindow( display, id );
325                     XUNLOCK;
326                 }
327             }
328             XLOCK;
329             XRaiseWindow( display, Wnd );
330             XUNLOCK;
331             
332             button = 0;
333             if( ((XButtonEvent *)p2 )->state & ControlMask )
334             {
335                 button |= KEY_CTRL;
336             } 
337             if( ((XButtonEvent *)p2 )->state & ShiftMask )
338             {
339                 button |= KEY_SHIFT;
340             }
341
342             switch( ( (XButtonEvent *)p2 )->button )
343             {
344                 case 1:
345                     // Left button
346                     button |= MOUSE_LEFT;
347                     time = OSAPI_GetTime();
348                     OSAPI_GetMousePos( posX, posY );
349                     if( time - ClickedTime < DblClickDelay && 
350                         posX == ClickedX && posY == ClickedY )
351                     {
352                         // Double-click
353                         ClickedTime = 0; 
354                         MouseDblClick( (int)( (XButtonEvent *)p2 )->x,
355                                        (int)( (XButtonEvent *)p2 )->y, button );
356                     }
357                     else
358                     {
359                         ClickedTime = time;
360                         ClickedX = posX;
361                         ClickedY = posY;
362                         LButtonDown = true;
363                         MouseDown( (int)( (XButtonEvent *)p2 )->x,
364                                    (int)( (XButtonEvent *)p2 )->y, button );
365                     }
366                     break;
367
368                 case 3:
369                     // Right button
370                     button |= MOUSE_RIGHT;
371                     RButtonDown = true;
372                     MouseDown( (int)( (XButtonEvent *)p2 )->x,
373                                (int)( (XButtonEvent *)p2 )->y, button );
374                     break;
375
376                 default:
377                     break;
378             }
379             return true;
380
381         case ButtonRelease:
382             button = 0;
383             if( ((XButtonEvent *)p2 )->state & ControlMask )
384             {
385                 button |= KEY_CTRL;
386             }
387             if( ((XButtonEvent *)p2 )->state & ShiftMask )
388             {
389                 button |= KEY_SHIFT;
390             }
391
392             switch( ( (XButtonEvent *)p2 )->button )
393             {
394                 case 1:
395                     // Left button
396                     button |= MOUSE_LEFT;
397                     LButtonDown = false;
398                     MouseUp( (int)( (XButtonEvent *)p2 )->x,
399                              (int)( (XButtonEvent *)p2 )->y, button );
400                     break;
401
402                 case 3:
403                     button |= MOUSE_RIGHT;
404                     // Right button
405                     RButtonDown = false;
406                     MouseUp( (int)( (XButtonEvent *)p2 )->x,
407                              (int)( (XButtonEvent *)p2 )->y, button );
408                     break; 
409
410                 case 4:
411                     // Scroll up
412                     MouseScroll( (int)( (XButtonEvent *)p2 )->x,
413                                  (int)( (XButtonEvent *)p2 )->y,
414                                  MOUSE_SCROLL_UP);
415                     break;
416  
417                 case 5:
418                     // Scroll down
419                     MouseScroll( (int)( (XButtonEvent *)p2 )->x,
420                                  (int)( (XButtonEvent *)p2 )->y,
421                                  MOUSE_SCROLL_DOWN);
422                     break;
423
424                 default:
425                     break;
426             }
427             return true;
428
429         case LeaveNotify:
430             OSAPI_PostMessage( this, WINDOW_LEAVE, 0, 0 );
431             return true;
432
433         case ClientMessage:
434             XLOCK;
435             type = XGetAtomName( display, ( (XClientMessageEvent*)
436                                              p2 )->message_type );
437             XUNLOCK;
438             if( type == "XdndEnter" )
439             {
440                 DropObject->DndEnter( ((XClientMessageEvent*)p2)->data.l );
441                 return true;
442             }
443             else if( type == "XdndPosition" )
444             {
445                 DropObject->DndPosition( ((XClientMessageEvent*)p2)->data.l );
446                 return true;
447             }
448             else if( type == "XdndLeave" )
449             {
450                 DropObject->DndLeave( ((XClientMessageEvent*)p2)->data.l );
451                 return true;
452             }
453             else if( type == "XdndDrop" )
454             {
455                 DropObject->DndDrop( ((XClientMessageEvent*)p2)->data.l );
456                 return true;
457             }
458             return false;
459             
460         default:
461             return false;
462     }
463 }
464 //---------------------------------------------------------------------------
465 void X11Window::SetTransparency( int Value )
466 {
467 /*    if( Value > -1 )
468         Alpha = Value;
469     SetLayeredWindowAttributes( hWnd, 0, Alpha, LWA_ALPHA | LWA_COLORKEY );
470     UpdateWindow( hWnd );*/
471 }
472 //---------------------------------------------------------------------------
473 void X11Window::RefreshFromImage( int x, int y, int w, int h )
474 {
475     Drawable drawable = (( X11Graphics* )Image )->GetImage();
476
477     XLOCK;
478     XCopyArea( display, drawable, Wnd, Gc, x, y, w, h, x, y );
479     XSync( display, 0);
480     XUNLOCK;
481 }
482 //---------------------------------------------------------------------------
483 void X11Window::WindowManualMove()
484 {
485     // Get mouse cursor position
486     int x, y;
487     OSAPI_GetMousePos( x, y );
488
489     // Move window and chek for magnetism
490     p_intf->p_sys->p_theme->MoveSkinMagnet( this,
491         WindowX + x - CursorX, WindowY + y - CursorY );
492
493 }
494 //---------------------------------------------------------------------------
495 void X11Window::WindowManualMoveInit()
496 {
497     OSAPI_GetMousePos( CursorX, CursorY );
498     WindowX = Left;
499     WindowY = Top;
500 }
501 //---------------------------------------------------------------------------
502 void X11Window::Move( int left, int top )
503 {
504     Left = left;
505     Top  = top;
506     XMoveWindow( display, Wnd, left, top );
507 }
508 //---------------------------------------------------------------------------
509 void X11Window::Size( int width, int height )
510 {
511     Width  = width;
512     Height = height;
513     XResizeWindow( display, Wnd, width, height );
514 }
515 //---------------------------------------------------------------------------
516
517
518 bool ToolTipCallback( void *data )
519 {
520     int direction, fontAscent, fontDescent;
521
522     Display *disp = ((tooltip_t*)data)->display;
523     Window win = ((tooltip_t*)data)->window;
524     Font font = ((tooltip_t*)data)->font;
525     GC gc = ((tooltip_t*)data)->gc;
526     string text = ((tooltip_t*)data)->text;
527     int curX = ((tooltip_t*)data)->curX;
528     int curY = ((tooltip_t*)data)->curY;
529  
530     XLOCK;
531     XClearWindow( disp, win );
532     XCharStruct overall;
533     XQueryTextExtents( disp, font, text.c_str(), text.size(), &direction, 
534                        &fontAscent, &fontDescent, &overall );
535     int w = overall.rbearing - overall.lbearing;
536     int h = overall.ascent + overall.descent;
537     XMapRaised( disp, win );
538     XMoveWindow( disp, win, curX - w/4, curY + 20 );
539     XResizeWindow( disp, win, w+8, h+8 );
540     XDrawString( disp, win, gc, 4, overall.ascent+4, text.c_str(), 
541                  text.size() );
542     XSync( disp, 0 );
543     ((tooltip_t*)data)->active = True;
544     XUNLOCK;
545     
546     return False;
547 }
548
549
550
551 void DrawToolTipText( tooltip_t *tooltip )
552 {
553     int direction, fontAscent, fontDescent;
554
555     Display *disp = tooltip->display;
556     Window win = tooltip->window;
557     Font font = tooltip->font;
558     GC gc = tooltip->gc;
559     string text = tooltip->text;
560     int curX = tooltip->curX;
561     int curY = tooltip->curY;
562  
563     XLOCK;
564     XClearWindow( disp, win );
565     XCharStruct overall;
566     XQueryTextExtents( disp, font, text.c_str(), text.size(), &direction, 
567                        &fontAscent, &fontDescent, &overall );
568     int w = overall.rbearing - overall.lbearing;
569     int h = overall.ascent + overall.descent;
570     XMoveWindow( disp, win, curX - w/4, curY + 20 );
571     XResizeWindow( disp, win, w+8, h+8 );
572     XDrawString( disp, win, gc, 4, overall.ascent+4, text.c_str(), 
573                  text.size() );
574     XSync( disp, 0 );
575     XUNLOCK;
576 }
577
578
579 void X11Window::ChangeToolTipText( string text )
580 {
581     if( text == "none" )
582     {
583         if( ToolTipText != "none" )
584         {
585             ToolTipText = "none";
586             XLOCK;
587             // Hide the tooltip window
588             X11TimerManager *timerManager = X11TimerManager::Instance( p_intf );
589             timerManager->removeTimer( ToolTip.timer );
590             XUnmapWindow( display, ToolTip.window );
591             XResizeWindow( display, ToolTip.window, 1, 1 );
592             XSync( display, 0 );
593             ToolTip.active = False;
594             XUNLOCK;
595         }
596     }
597     else
598     {
599         if( text != ToolTipText )
600         {
601             ToolTipText = text;
602             ToolTip.text = text;
603             if( !ToolTip.active )
604             {
605                 // Create the tooltip
606                 OSAPI_GetMousePos( ToolTip.curX, ToolTip.curY );
607                 X11TimerManager *timerManager = X11TimerManager::Instance( p_intf );
608                 timerManager->addTimer( ToolTip.timer );
609             }
610             else
611             {
612                 // Refresh the tooltip
613                 DrawToolTipText( &ToolTip );
614             }
615         }
616     }
617 }
618 //---------------------------------------------------------------------------
619
620 #endif