* X11 skin windows should have an icon now
[vlc.git] / 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.22 2003/06/13 21:18:54 asmax 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 bool ToolTipCallback( void *data );
55
56
57 //---------------------------------------------------------------------------
58 // Skinable Window
59 //---------------------------------------------------------------------------
60 X11Window::X11Window( intf_thread_t *p_intf, Window wnd, int x, int y,
61     bool visible, int transition, int normalalpha, int movealpha,
62     bool dragdrop, string name )
63     : SkinWindow( p_intf, x, y, visible, transition, normalalpha, movealpha,
64               dragdrop )
65 {
66     // Set handles
67     Wnd         = wnd;
68     display     = p_intf->p_sys->display;
69     int screen  = DefaultScreen( display );
70     Name        = name;
71     LButtonDown = false;
72     RButtonDown = false;
73
74     // Creation of a graphic context that doesn't generate a GraphicsExpose
75     // event when using functions like XCopyArea
76     XGCValues gcVal;
77     gcVal.graphics_exposures = False;
78     XLOCK;
79     Gc = XCreateGC( display, wnd, GCGraphicsExposures, &gcVal );
80     XUNLOCK;
81
82     // Removing fading effect
83     Transition  = 0;
84
85     if( DragDrop )
86     {
87         // register the listview as a drop target
88         DropObject = new X11DropObject( p_intf, Wnd );
89
90         Atom xdndAtom = XInternAtom( display, "XdndAware", False );
91         char xdndVersion = 4;
92         XLOCK;
93         XChangeProperty( display, wnd, xdndAtom, XA_ATOM, 32, 
94                          PropModeReplace, (unsigned char *)&xdndVersion, 1);
95         XUNLOCK;
96     }
97
98     // Associate vlc icon to the window
99     XLOCK;
100     XWMHints *hints = XGetWMHints( display, Wnd );
101     if( !hints)
102     {
103         hints = XAllocWMHints();
104     }
105     if( hints->icon_pixmap != None )
106     {
107         hints->icon_pixmap = p_intf->p_sys->iconPixmap;
108         hints->flags |= IconPixmapHint;
109     }
110     if( hints->icon_mask != None )
111     {
112         hints->icon_mask = p_intf->p_sys->iconMask;
113         hints->flags |= IconMaskHint;
114     }
115     XSetWMHints( display, Wnd, hints );
116     XFree( hints );
117     XUNLOCK;
118
119     // Create Tool Tip window
120     XColor color;
121     color.red = 0xffff;
122     color.green = 0xffff;
123     color.blue = 0xa000;
124     Colormap cm = DefaultColormap( display, screen );
125     Window root = DefaultRootWindow( display );
126
127     XLOCK;
128     XAllocColor( display, cm, &color );
129     XSetWindowAttributes attr;
130     attr.background_pixel = color.pixel;
131     attr.override_redirect = True;
132     ToolTip.window = XCreateWindow( display, root, 0, 0, 1, 1, 1, 0,
133                                     InputOutput, CopyFromParent,
134                                     CWBackPixel|CWOverrideRedirect, &attr );
135     ToolTip.font = XLoadFont( display,
136                               "-*-helvetica-bold-r-*-*-*-80-*-*-*-*-*-*" );
137     gcVal.font = ToolTip.font;
138     gcVal.foreground = 0;
139     gcVal.background = color.pixel;
140     ToolTip.gc = XCreateGC( display, ToolTip.window, 
141                             GCBackground|GCForeground|GCFont, &gcVal );
142     XUNLOCK;
143
144     ToolTip.display = display;
145     X11Timer *timer = new X11Timer( p_intf, 500000, ToolTipCallback, &ToolTip );
146     ToolTip.p_intf = p_intf;
147     ToolTip.timer = timer;
148
149     // Double-click handling
150     ClickedX = 0;
151     ClickedY = 0;
152     ClickedTime = 0;
153     // TODO: can be retrieved somewhere ?
154     DblClickDelay = 400;
155    
156 }
157 //---------------------------------------------------------------------------
158 X11Window::~X11Window()
159 {
160     if( DragDrop )
161     {
162         delete DropObject;
163     }
164     delete ToolTip.timer;
165     XLOCK;
166     XFreeGC( display, ToolTip.gc );
167     XFreeGC( display, Gc );
168     XDestroyWindow( display, ToolTip.window );
169     XDestroyWindow( display, Wnd );
170     XUNLOCK;
171 }
172 //---------------------------------------------------------------------------
173 void X11Window::OSShow( bool show )
174 {
175     XLOCK;
176     XResizeWindow( display, Wnd, 1, 1 ); // Avoid flicker
177     XUNLOCK;
178
179     if( show )
180     {
181         // We do the call to XShapeCombineRegion() here because the window
182         // must be unmapped for this to work.
183         Drawable drawable = (( X11Graphics* )Image )->GetImage();
184
185         XLOCK;
186         XImage *image = XGetImage( display, drawable, 0, 0, Width, Height, 
187                                    AllPlanes, ZPixmap );
188         if( image )
189         {
190             // Mask for transparency
191             Region region = XCreateRegion();
192             for( int line = 0; line < Height; line++ )
193             {
194                 int start = 0, end = 0;
195                 while( start < Width )
196                 {
197                     while( start < Width && XGetPixel( image, start, line )
198                            == 0 )
199                     {
200                         start++;
201                     }
202                     end = start;
203                     while( end < Width && XGetPixel( image, end, line ) != 0)
204                     {
205                         end++;
206                     }
207                     XRectangle rect;
208                     rect.x = start;
209                     rect.y = line;
210                     rect.width = end - start + 1;
211                     rect.height = 1;
212                     Region newRegion = XCreateRegion();
213                     XUnionRectWithRegion( &rect, region, newRegion );
214                     XDestroyRegion( region );
215                     region = newRegion;
216                     start = end + 1;
217                 }
218             }
219             XDestroyImage( image );
220
221             XShapeCombineRegion( display, Wnd, ShapeBounding, 0, 0, region,
222                                  ShapeSet );
223             XDestroyRegion( region );
224
225         }
226         else
227         {
228             msg_Err( p_intf, "X11Window::OSShow XShapeCombineRegion() failed");
229         }
230
231         XMapWindow( display, Wnd );
232         XMoveResizeWindow( display, Wnd, Left, Top, Width, Height );
233         XUNLOCK;
234     }
235     else
236     {
237         XLOCK;
238         XUnmapWindow( display, Wnd );
239         XUNLOCK;
240     }
241 }
242 //---------------------------------------------------------------------------
243 bool X11Window::ProcessOSEvent( Event *evt )
244 {
245     unsigned int msg = evt->GetMessage();
246     //unsigned int p1  = evt->GetParam1();
247     int          p2  = evt->GetParam2();
248     int          time;
249     int          posX, posY;
250     string       type;
251     int          button;
252
253     switch( msg )
254     {
255         case Expose:
256             RefreshFromImage( 0, 0, Width, Height );
257             return true;
258
259         case MotionNotify:
260             if( LButtonDown )
261                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
262                            (int)( (XMotionEvent *)p2 )->y, 1 );
263             else if( RButtonDown )
264                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
265                            (int)( (XMotionEvent *)p2 )->y, 2 );
266             else
267                 MouseMove( (int)( (XMotionEvent *)p2 )->x,
268                            (int)( (XMotionEvent *)p2 )->y, 0 );
269             return true;
270
271
272         case ButtonPress:
273             // Raise all the windows
274             for( list<SkinWindow *>::const_iterator win = 
275                     p_intf->p_sys->p_theme->WindowList.begin();
276                     win != p_intf->p_sys->p_theme->WindowList.end(); win++ )
277             {
278                 XLOCK;
279                 XRaiseWindow( display, ( (X11Window *)(*win) )->GetHandle() );
280                 XUNLOCK;
281             }
282             
283             button = 0;
284             if( ((XButtonEvent *)p2 )->state & ControlMask )
285             {
286                 button |= KEY_CTRL;
287             } 
288             if( ((XButtonEvent *)p2 )->state & ShiftMask )
289             {
290                 button |= KEY_SHIFT;
291             }
292
293             switch( ( (XButtonEvent *)p2 )->button )
294             {
295                 case 1:
296                     // Left button
297                     button |= MOUSE_LEFT;
298                     time = OSAPI_GetTime();
299                     OSAPI_GetMousePos( posX, posY );
300                     if( time - ClickedTime < DblClickDelay && 
301                         posX == ClickedX && posY == ClickedY )
302                     {
303                         // Double-click
304                         ClickedTime = 0; 
305                         MouseDblClick( (int)( (XButtonEvent *)p2 )->x,
306                                        (int)( (XButtonEvent *)p2 )->y, button );
307                     }
308                     else
309                     {
310                         ClickedTime = time;
311                         ClickedX = posX;
312                         ClickedY = posY;
313                         LButtonDown = true;
314                         MouseDown( (int)( (XButtonEvent *)p2 )->x,
315                                    (int)( (XButtonEvent *)p2 )->y, button );
316                     }
317                     break;
318
319                 case 3:
320                     // Right button
321                     button |= MOUSE_RIGHT;
322                     RButtonDown = true;
323                     MouseDown( (int)( (XButtonEvent *)p2 )->x,
324                                (int)( (XButtonEvent *)p2 )->y, button );
325                     break;
326
327                 default:
328                     break;
329             }
330             return true;
331
332         case ButtonRelease:
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                     LButtonDown = false;
349                     MouseUp( (int)( (XButtonEvent *)p2 )->x,
350                              (int)( (XButtonEvent *)p2 )->y, button );
351                     break;
352
353                 case 3:
354                     button |= MOUSE_RIGHT;
355                     // Right button
356                     RButtonDown = false;
357                     MouseUp( (int)( (XButtonEvent *)p2 )->x,
358                              (int)( (XButtonEvent *)p2 )->y, button );
359                     break; 
360
361                 case 4:
362                     // Scroll up
363                     MouseScroll( (int)( (XButtonEvent *)p2 )->x,
364                                  (int)( (XButtonEvent *)p2 )->y,
365                                  MOUSE_SCROLL_UP);
366                     break;
367  
368                 case 5:
369                     // Scroll down
370                     MouseScroll( (int)( (XButtonEvent *)p2 )->x,
371                                  (int)( (XButtonEvent *)p2 )->y,
372                                  MOUSE_SCROLL_DOWN);
373                     break;
374
375                 default:
376                     break;
377             }
378             return true;
379
380         case LeaveNotify:
381             OSAPI_PostMessage( this, WINDOW_LEAVE, 0, 0 );
382             return true;
383
384         case ClientMessage:
385             XLOCK;
386             type = XGetAtomName( display, ( (XClientMessageEvent*)
387                                              p2 )->message_type );
388             XUNLOCK;
389             if( type == "XdndEnter" )
390             {
391                 DropObject->DndEnter( ((XClientMessageEvent*)p2)->data.l );
392                 return true;
393             }
394             else if( type == "XdndPosition" )
395             {
396                 DropObject->DndPosition( ((XClientMessageEvent*)p2)->data.l );
397                 return true;
398             }
399             else if( type == "XdndLeave" )
400             {
401                 DropObject->DndLeave( ((XClientMessageEvent*)p2)->data.l );
402                 return true;
403             }
404             else if( type == "XdndDrop" )
405             {
406                 DropObject->DndDrop( ((XClientMessageEvent*)p2)->data.l );
407                 return true;
408             }
409             return false;
410             
411         default:
412             return false;
413     }
414 }
415 //---------------------------------------------------------------------------
416 void X11Window::SetTransparency( int Value )
417 {
418 /*    if( Value > -1 )
419         Alpha = Value;
420     SetLayeredWindowAttributes( hWnd, 0, Alpha, LWA_ALPHA | LWA_COLORKEY );
421     UpdateWindow( hWnd );*/
422 }
423 //---------------------------------------------------------------------------
424 void X11Window::RefreshFromImage( int x, int y, int w, int h )
425 {
426     Drawable drawable = (( X11Graphics* )Image )->GetImage();
427
428     XLOCK;
429     XCopyArea( display, drawable, Wnd, Gc, x, y, w, h, x, y );
430     XSync( display, 0);
431     XUNLOCK;
432 }
433 //---------------------------------------------------------------------------
434 void X11Window::WindowManualMove()
435 {
436     // Get mouse cursor position
437     int x, y;
438     OSAPI_GetMousePos( x, y );
439
440     // Move window and chek for magnetism
441     p_intf->p_sys->p_theme->MoveSkinMagnet( this,
442         WindowX + x - CursorX, WindowY + y - CursorY );
443
444 }
445 //---------------------------------------------------------------------------
446 void X11Window::WindowManualMoveInit()
447 {
448     OSAPI_GetMousePos( CursorX, CursorY );
449     WindowX = Left;
450     WindowY = Top;
451 }
452 //---------------------------------------------------------------------------
453 void X11Window::Move( int left, int top )
454 {
455     Left = left;
456     Top  = top;
457     XMoveWindow( display, Wnd, left, top );
458 }
459 //---------------------------------------------------------------------------
460 void X11Window::Size( int width, int height )
461 {
462     Width  = width;
463     Height = height;
464     XResizeWindow( display, Wnd, width, height );
465 }
466 //---------------------------------------------------------------------------
467
468
469 bool ToolTipCallback( void *data )
470 {
471     int direction, fontAscent, fontDescent;
472
473     Display *disp = ((tooltip_t*)data)->display;
474     Window win = ((tooltip_t*)data)->window;
475     Font font = ((tooltip_t*)data)->font;
476     GC gc = ((tooltip_t*)data)->gc;
477     string text = ((tooltip_t*)data)->text;
478     int curX = ((tooltip_t*)data)->curX;
479     int curY = ((tooltip_t*)data)->curY;
480  
481     XLOCK;
482     XClearWindow( disp, win );
483     XCharStruct overall;
484     XQueryTextExtents( disp, font, text.c_str(), text.size(), &direction, 
485                        &fontAscent, &fontDescent, &overall );
486     int w = overall.rbearing - overall.lbearing;
487     int h = overall.ascent + overall.descent;
488     XMapRaised( disp, win );
489     XMoveWindow( disp, win, curX - w/4, curY + 20 );
490     XResizeWindow( disp, win, w+8, h+8 );
491     XDrawString( disp, win, gc, 4, overall.ascent+4, text.c_str(), 
492                  text.size() );
493     XSync( disp, 0 );
494     XUNLOCK;
495     
496     return False;
497 }
498
499
500 void X11Window::ChangeToolTipText( string text )
501 {
502     if( text == "none" )
503     {
504         if( ToolTipText != "none" )
505         {
506             ToolTipText = "none";
507             XLOCK;
508             // Hide the tooltip window
509             X11TimerManager *timerManager = X11TimerManager::Instance( p_intf );
510             timerManager->removeTimer( ToolTip.timer );
511             XUnmapWindow( display, ToolTip.window );
512             XResizeWindow( display, ToolTip.window, 1, 1 );
513             XSync( display, 0 );
514             XUNLOCK;
515         }
516     }
517     else
518     {
519         if( text != ToolTipText )
520         {
521             ToolTipText = text;
522             ToolTip.text = text;
523             OSAPI_GetMousePos( ToolTip.curX, ToolTip.curY );
524             X11TimerManager *timerManager = X11TimerManager::Instance( p_intf );
525             timerManager->addTimer( ToolTip.timer );
526         }
527     }
528 }
529 //---------------------------------------------------------------------------
530
531 #endif