]> git.sesse.net Git - vlc/blob - modules/gui/skins/x11/x11_window.cpp
b051ece8266020894de0de4e6b5ff8e5fd850d35
[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.29 2003/10/19 22:25:10 gbazin 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, 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 );
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 //---------------------------------------------------------------------------
160 X11Window::~X11Window()
161 {
162     if( DragDrop )
163     {
164         delete DropObject;
165     }
166     delete ToolTip.timer;
167     XLOCK;
168     XFreeGC( display, ToolTip.gc );
169     XFreeGC( display, Gc );
170     XDestroyWindow( display, ToolTip.window );
171     XDestroyWindow( display, Wnd );
172     XUNLOCK;
173 }
174 //---------------------------------------------------------------------------
175 void X11Window::ToggleOnTop()
176 {
177     XEvent ev;
178     ev.type = ClientMessage;
179     ev.xclient.display = display;
180     ev.xclient.window = Wnd;
181     ev.xclient.serial = 0;
182     ev.xclient.send_event = True;
183     ev.xclient.message_type = XInternAtom( display, "_NET_WM_STATE", True );
184     ev.xclient.format = 32;
185     ev.xclient.data.l[1] = XInternAtom( display, "_NET_WM_STATE_ABOVE", False);
186     ev.xclient.data.l[2] = 0;
187     ev.xclient.data.l[3] = 0;
188     ev.xclient.data.l[4] = 0;
189
190     if( !p_intf->p_sys->b_on_top )
191     {
192         // Set the window on top
193         ev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
194     }
195     else
196     {
197         // Set the window not on top
198         ev.xclient.data.l[0] = 0; // _NET_WM_STATE_REMOVE
199     }
200
201     XLOCK;
202     XSendEvent( display, DefaultRootWindow( display ), False,
203                 SubstructureRedirectMask | SubstructureNotifyMask, &ev );
204     XUNLOCK;
205
206     /* For KDE */
207     ev.xclient.data.l[1] = XInternAtom( display, "_NET_WM_STATE_STAYS_ON_TOP",
208                                         False);
209     XLOCK;
210     XSendEvent( display, DefaultRootWindow( display ), False,
211                 SubstructureRedirectMask | SubstructureNotifyMask, &ev );
212     XUNLOCK;
213 }
214 //---------------------------------------------------------------------------
215 void X11Window::OSShow( bool show )
216 {
217     XLOCK;
218     XResizeWindow( display, Wnd, 1, 1 ); // Avoid flicker
219     XUNLOCK;
220
221     if( show )
222     {
223         // We do the call to XShapeCombineRegion() here because the window
224         // must be unmapped for this to work.
225         Drawable drawable = (( X11Graphics* )Image )->GetImage();
226
227         XLOCK;
228         XImage *image = XGetImage( display, drawable, 0, 0, Width, Height, 
229                                    AllPlanes, ZPixmap );
230         if( image )
231         {
232             // Mask for transparency
233             Region region = XCreateRegion();
234             for( int line = 0; line < Height; line++ )
235             {
236                 int start = 0, end = 0;
237                 while( start < Width )
238                 {
239                     while( start < Width && XGetPixel( image, start, line )
240                            == 0 )
241                     {
242                         start++;
243                     }
244                     end = start;
245                     while( end < Width && XGetPixel( image, end, line ) != 0)
246                     {
247                         end++;
248                     }
249                     XRectangle rect;
250                     rect.x = start;
251                     rect.y = line;
252                     rect.width = end - start + 1;
253                     rect.height = 1;
254                     Region newRegion = XCreateRegion();
255                     XUnionRectWithRegion( &rect, region, newRegion );
256                     XDestroyRegion( region );
257                     region = newRegion;
258                     start = end + 1;
259                 }
260             }
261             XDestroyImage( image );
262
263             XShapeCombineRegion( display, Wnd, ShapeBounding, 0, 0, region,
264                                  ShapeSet );
265             XDestroyRegion( region );
266
267         }
268         else
269         {
270             msg_Err( p_intf, "X11Window::OSShow XShapeCombineRegion() failed");
271         }
272
273         XMapWindow( display, Wnd );
274         XMoveResizeWindow( display, Wnd, Left, Top, Width, Height );
275         XUNLOCK;
276     }
277     else
278     {
279         XLOCK;
280         XUnmapWindow( display, Wnd );
281         XUNLOCK;
282     }
283 }
284 //---------------------------------------------------------------------------
285 bool X11Window::ProcessOSEvent( Event *evt )
286 {
287     unsigned int msg = evt->GetMessage();
288     //unsigned int p1  = evt->GetParam1();
289     int          p2  = evt->GetParam2();
290     int          time;
291     int          posX, posY;
292     string       type;
293     int          button;
294
295     switch( msg )
296     {
297         case Expose:
298             RefreshFromImage( 0, 0, Width, Height );
299             return true;
300
301         case MotionNotify:
302             if( LButtonDown )
303                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
304                            (int)( (XMotionEvent *)p2 )->y, 1 );
305             else if( RButtonDown )
306                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
307                            (int)( (XMotionEvent *)p2 )->y, 2 );
308             else
309                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
310                            (int)( (XMotionEvent *)p2 )->y, 0 );
311             return true;
312
313
314         case ButtonPress:
315             // Raise all the windows
316             for( list<SkinWindow *>::const_iterator win = 
317                     p_intf->p_sys->p_theme->WindowList.begin();
318                     win != p_intf->p_sys->p_theme->WindowList.end(); win++ )
319             {
320                 Window id = ( (X11Window *)(*win) )->GetHandle();
321                 // the current window is raised last
322                 if( id != Wnd )
323                 {
324                     XLOCK;
325                     XRaiseWindow( display, id );
326                     XUNLOCK;
327                 }
328             }
329             XLOCK;
330             XRaiseWindow( display, Wnd );
331             XUNLOCK;
332             
333             button = 0;
334             if( ((XButtonEvent *)p2 )->state & ControlMask )
335             {
336                 button |= KEY_CTRL;
337             } 
338             if( ((XButtonEvent *)p2 )->state & ShiftMask )
339             {
340                 button |= KEY_SHIFT;
341             }
342
343             switch( ( (XButtonEvent *)p2 )->button )
344             {
345                 case 1:
346                     // Left button
347                     button |= MOUSE_LEFT;
348                     time = OSAPI_GetTime();
349                     OSAPI_GetMousePos( posX, posY );
350                     if( time - ClickedTime < DblClickDelay && 
351                         posX == ClickedX && posY == ClickedY )
352                     {
353                         // Double-click
354                         ClickedTime = 0; 
355                         MouseDblClick( (int)( (XButtonEvent *)p2 )->x,
356                                        (int)( (XButtonEvent *)p2 )->y, button );
357                     }
358                     else
359                     {
360                         ClickedTime = time;
361                         ClickedX = posX;
362                         ClickedY = posY;
363                         LButtonDown = true;
364                         MouseDown( (int)( (XButtonEvent *)p2 )->x,
365                                    (int)( (XButtonEvent *)p2 )->y, button );
366                     }
367                     break;
368
369                 case 3:
370                     // Right button
371                     button |= MOUSE_RIGHT;
372                     RButtonDown = true;
373                     MouseDown( (int)( (XButtonEvent *)p2 )->x,
374                                (int)( (XButtonEvent *)p2 )->y, button );
375                     break;
376
377                 default:
378                     break;
379             }
380             return true;
381
382         case ButtonRelease:
383             button = 0;
384             if( ((XButtonEvent *)p2 )->state & ControlMask )
385             {
386                 button |= KEY_CTRL;
387             }
388             if( ((XButtonEvent *)p2 )->state & ShiftMask )
389             {
390                 button |= KEY_SHIFT;
391             }
392
393             switch( ( (XButtonEvent *)p2 )->button )
394             {
395                 case 1:
396                     // Left button
397                     button |= MOUSE_LEFT;
398                     LButtonDown = false;
399                     MouseUp( (int)( (XButtonEvent *)p2 )->x,
400                              (int)( (XButtonEvent *)p2 )->y, button );
401                     break;
402
403                 case 3:
404                     button |= MOUSE_RIGHT;
405                     // Right button
406                     RButtonDown = false;
407                     MouseUp( (int)( (XButtonEvent *)p2 )->x,
408                              (int)( (XButtonEvent *)p2 )->y, button );
409                     break; 
410
411                 case 4:
412                     // Scroll up
413                     MouseScroll( (int)( (XButtonEvent *)p2 )->x,
414                                  (int)( (XButtonEvent *)p2 )->y,
415                                  MOUSE_SCROLL_UP);
416                     break;
417  
418                 case 5:
419                     // Scroll down
420                     MouseScroll( (int)( (XButtonEvent *)p2 )->x,
421                                  (int)( (XButtonEvent *)p2 )->y,
422                                  MOUSE_SCROLL_DOWN);
423                     break;
424
425                 default:
426                     break;
427             }
428             return true;
429
430         case LeaveNotify:
431             OSAPI_PostMessage( this, WINDOW_LEAVE, 0, 0 );
432             return true;
433
434         case ClientMessage:
435             XLOCK;
436             type = XGetAtomName( display, ( (XClientMessageEvent*)
437                                              p2 )->message_type );
438             XUNLOCK;
439             if( type == "XdndEnter" )
440             {
441                 DropObject->DndEnter( ((XClientMessageEvent*)p2)->data.l );
442                 return true;
443             }
444             else if( type == "XdndPosition" )
445             {
446                 DropObject->DndPosition( ((XClientMessageEvent*)p2)->data.l );
447                 return true;
448             }
449             else if( type == "XdndLeave" )
450             {
451                 DropObject->DndLeave( ((XClientMessageEvent*)p2)->data.l );
452                 return true;
453             }
454             else if( type == "XdndDrop" )
455             {
456                 DropObject->DndDrop( ((XClientMessageEvent*)p2)->data.l );
457                 return true;
458             }
459             return false;
460             
461         default:
462             return false;
463     }
464 }
465 //---------------------------------------------------------------------------
466 void X11Window::SetTransparency( int Value )
467 {
468 /*    if( Value > -1 )
469         Alpha = Value;
470     SetLayeredWindowAttributes( hWnd, 0, Alpha, LWA_ALPHA | LWA_COLORKEY );
471     UpdateWindow( hWnd );*/
472 }
473 //---------------------------------------------------------------------------
474 void X11Window::RefreshFromImage( int x, int y, int w, int h )
475 {
476     Drawable drawable = (( X11Graphics* )Image )->GetImage();
477
478     XLOCK;
479     XCopyArea( display, drawable, Wnd, Gc, x, y, w, h, x, y );
480     XSync( display, 0);
481     XUNLOCK;
482 }
483 //---------------------------------------------------------------------------
484 void X11Window::WindowManualMove()
485 {
486     // Get mouse cursor position
487     int x, y;
488     OSAPI_GetMousePos( x, y );
489
490     // Move window and chek for magnetism
491     p_intf->p_sys->p_theme->MoveSkinMagnet( this,
492         WindowX + x - CursorX, WindowY + y - CursorY );
493
494 }
495 //---------------------------------------------------------------------------
496 void X11Window::WindowManualMoveInit()
497 {
498     OSAPI_GetMousePos( CursorX, CursorY );
499     WindowX = Left;
500     WindowY = Top;
501 }
502 //---------------------------------------------------------------------------
503 void X11Window::Move( int left, int top )
504 {
505     Left = left;
506     Top  = top;
507     XMoveWindow( display, Wnd, left, top );
508 }
509 //---------------------------------------------------------------------------
510 void X11Window::Size( int width, int height )
511 {
512     Width  = width;
513     Height = height;
514     XResizeWindow( display, Wnd, width, height );
515 }
516 //---------------------------------------------------------------------------
517
518
519 bool ToolTipCallback( void *data )
520 {
521     int direction, fontAscent, fontDescent;
522
523     Display *disp = ((tooltip_t*)data)->display;
524     Window win = ((tooltip_t*)data)->window;
525     Font font = ((tooltip_t*)data)->font;
526     GC gc = ((tooltip_t*)data)->gc;
527     string text = ((tooltip_t*)data)->text;
528     int curX = ((tooltip_t*)data)->curX;
529     int curY = ((tooltip_t*)data)->curY;
530  
531     XLOCK;
532     XClearWindow( disp, win );
533     XCharStruct overall;
534     XQueryTextExtents( disp, font, text.c_str(), text.size(), &direction, 
535                        &fontAscent, &fontDescent, &overall );
536     int w = overall.rbearing - overall.lbearing;
537     int h = overall.ascent + overall.descent;
538     XMapRaised( disp, win );
539     XMoveWindow( disp, win, curX - w/4, curY + 20 );
540     XResizeWindow( disp, win, w+8, h+8 );
541     XDrawString( disp, win, gc, 4, overall.ascent+4, text.c_str(), 
542                  text.size() );
543     XSync( disp, 0 );
544     ((tooltip_t*)data)->active = True;
545     XUNLOCK;
546     
547     return False;
548 }
549
550
551
552 void DrawToolTipText( tooltip_t *tooltip )
553 {
554     int direction, fontAscent, fontDescent;
555
556     Display *disp = tooltip->display;
557     Window win = tooltip->window;
558     Font font = tooltip->font;
559     GC gc = tooltip->gc;
560     string text = tooltip->text;
561     int curX = tooltip->curX;
562     int curY = tooltip->curY;
563  
564     XLOCK;
565     XClearWindow( disp, win );
566     XCharStruct overall;
567     XQueryTextExtents( disp, font, text.c_str(), text.size(), &direction, 
568                        &fontAscent, &fontDescent, &overall );
569     int w = overall.rbearing - overall.lbearing;
570     int h = overall.ascent + overall.descent;
571     XMoveWindow( disp, win, curX - w/4, curY + 20 );
572     XResizeWindow( disp, win, w+8, h+8 );
573     XDrawString( disp, win, gc, 4, overall.ascent+4, text.c_str(), 
574                  text.size() );
575     XSync( disp, 0 );
576     XUNLOCK;
577 }
578
579
580 void X11Window::ChangeToolTipText( string text )
581 {
582     if( text == "none" )
583     {
584         if( ToolTipText != "none" )
585         {
586             ToolTipText = "none";
587             XLOCK;
588             // Hide the tooltip window
589             X11TimerManager *timerManager = X11TimerManager::Instance( p_intf );
590             timerManager->removeTimer( ToolTip.timer );
591             XUnmapWindow( display, ToolTip.window );
592             XResizeWindow( display, ToolTip.window, 1, 1 );
593             XSync( display, 0 );
594             ToolTip.active = False;
595             XUNLOCK;
596         }
597     }
598     else
599     {
600         if( text != ToolTipText )
601         {
602             ToolTipText = text;
603             ToolTip.text = text;
604             if( !ToolTip.active )
605             {
606                 // Create the tooltip
607                 OSAPI_GetMousePos( ToolTip.curX, ToolTip.curY );
608                 X11TimerManager *timerManager = X11TimerManager::Instance( p_intf );
609                 timerManager->addTimer( ToolTip.timer );
610             }
611             else
612             {
613                 // Refresh the tooltip
614                 DrawToolTipText( &ToolTip );
615             }
616         }
617     }
618 }
619 //---------------------------------------------------------------------------
620
621 #endif