Do not add a newline at the end of the strings send to msg_*
[vlc.git] / modules / gui / skins2 / x11 / x11_display.cpp
1 /*****************************************************************************
2  * x11_display.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
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 along
21  * with this program; if not, write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef X11_SKINS
26
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <X11/xpm.h>
30 #include <X11/extensions/shape.h>
31
32 #include "x11_display.hpp"
33 #include "../src/logger.hpp"
34
35 #include "../../../share/icons/32x32/vlc.xpm"
36
37 template<class type> type X11Display::putPixel(type r, type g, type b) const
38 {
39     return ( (r >> m_redRightShift)   << m_redLeftShift   ) |
40            ( (g >> m_greenRightShift) << m_greenLeftShift ) |
41            ( (b >> m_blueRightShift)  << m_blueLeftShift  );
42 }
43
44 template<class type>
45 type X11Display::blendPixel(type v,type r, type g, type b, type a) const
46 {
47     uint16_t temp;
48
49     temp = ((uint8_t)((v >> m_redLeftShift) << m_redRightShift));
50     uint8_t red = r + ( temp * (255 - a) ) / 255;
51     temp = ((uint8_t)((v >> m_greenLeftShift) << m_greenRightShift));
52     uint8_t green = g + ( temp * (255 - a) ) / 255;
53     temp = ((uint8_t)((v >> m_blueLeftShift) << m_blueRightShift));
54     uint8_t blue = b + ( temp * (255 - a) ) / 255;
55
56     return putPixel<type>(red,green,blue);
57 }
58
59 X11Display::X11Display( intf_thread_t *pIntf ): SkinObject( pIntf ),
60     m_mainWindow( 0 ), m_gc( NULL ), m_colormap( 0 )
61 {
62     // Open a connection to the X Server
63     m_pDisplay = XOpenDisplay( NULL );
64     if( m_pDisplay == NULL )
65     {
66         MSG_ERR( "Cannot open display" );
67         return;
68     }
69
70     // Load the XShape extension
71     int event, error;
72     XShapeQueryExtension( m_pDisplay, &event, &error );
73
74     // Get the display parameters
75     int screen = DefaultScreen( m_pDisplay );
76     int depth = DefaultDepth( m_pDisplay, screen );
77     int order = ImageByteOrder( m_pDisplay );
78     Window root = DefaultRootWindow( m_pDisplay );
79
80     // Template for looking up the XVisualInfo
81     XVisualInfo xVInfoTemplate;
82     xVInfoTemplate.screen = screen;
83     xVInfoTemplate.depth = depth;
84
85     XVisualInfo *pVInfo = NULL;
86     int vCount = 0;
87
88     switch( depth )
89     {
90     case 8:
91         xVInfoTemplate.c_class = DirectColor;
92         // Get the DirectColor visual
93         pVInfo = XGetVisualInfo( m_pDisplay, VisualScreenMask |
94                                  VisualClassMask, &xVInfoTemplate,
95                                  &vCount );
96         if( pVInfo == NULL )
97         {
98             msg_Err( getIntf(), "no DirectColor visual available" );
99             m_pDisplay = NULL;
100             break;
101         }
102         m_pVisual = pVInfo->visual;
103
104         // Compute the color shifts
105         getShifts( pVInfo->red_mask, m_redLeftShift, m_redRightShift );
106         getShifts( pVInfo->green_mask, m_greenLeftShift,
107                    m_greenRightShift );
108         getShifts( pVInfo->blue_mask, m_blueLeftShift, m_blueRightShift );
109
110         // Create a color map
111         m_colormap = XCreateColormap( m_pDisplay, root,
112                 DefaultVisual( m_pDisplay, screen ), AllocAll );
113
114         // Create the palette
115         XColor pColors[255];
116         for( uint16_t i = 0; i < 255; i++ )
117         {
118             // kludge: colors are indexed reversely because color 255 seems
119             // to bereserved for black even if we try to set it to white
120             pColors[i].pixel = 254-i;
121             pColors[i].pad   = 0;
122             pColors[i].flags = DoRed | DoGreen | DoBlue;
123             pColors[i].red   =
124                 (i >> m_redLeftShift) << (m_redRightShift + 8);
125             pColors[i].green =
126                 (i >> m_greenLeftShift) << (m_greenRightShift + 8);
127             pColors[i].blue  =
128                 (i >> m_blueLeftShift) << (m_blueRightShift + 8);
129         }
130         XStoreColors( m_pDisplay, m_colormap, pColors, 255 );
131         blendPixelImpl = &X11Display::blendPixel8;
132         putPixelImpl = &X11Display::putPixel8;
133         m_pixelSize = 1;
134         break;
135
136     case 15:
137     case 16:
138     case 24:
139     case 32:
140         // Get the TrueColor visual
141         xVInfoTemplate.c_class = TrueColor;
142         pVInfo = XGetVisualInfo( m_pDisplay, VisualScreenMask |
143                                  VisualDepthMask | VisualClassMask,
144                                  &xVInfoTemplate, &vCount );
145         if( pVInfo == NULL )
146         {
147             msg_Err( getIntf(), "No TrueColor visual for depth %d", depth );
148             m_pDisplay = NULL;
149             break;
150         }
151         m_pVisual = pVInfo->visual;
152
153         // Compute the color shifts
154         getShifts( pVInfo->red_mask,   m_redLeftShift,   m_redRightShift   );
155         getShifts( pVInfo->green_mask, m_greenLeftShift, m_greenRightShift );
156         getShifts( pVInfo->blue_mask,  m_blueLeftShift,  m_blueRightShift  );
157
158         if( depth == 15 || depth == 16 )
159         {
160             if( order == MSBFirst )
161             {
162                 blendPixelImpl = &X11Display::blendPixel16MSB;
163                 putPixelImpl = &X11Display::putPixel16MSB;
164             }
165             else
166             {
167                 blendPixelImpl = &X11Display::blendPixel16LSB;
168                 putPixelImpl = &X11Display::putPixel16LSB;
169             }
170             m_pixelSize = 2;
171         }
172         else
173         {
174             if( order == MSBFirst )
175             {
176                 blendPixelImpl = &X11Display::blendPixel32MSB;
177                 putPixelImpl = &X11Display::putPixel32MSB;
178             }
179             else
180             {
181                 blendPixelImpl = &X11Display::blendPixel32LSB;
182                 putPixelImpl = &X11Display::putPixel32LSB;
183             }
184             m_pixelSize = 4;
185         }
186         break;
187
188     default:
189         msg_Err( getIntf(), "unsupported depth: %d bpp", depth );
190         m_pDisplay = NULL;
191         break;
192     }
193
194     // Free the visual info
195     if( pVInfo )
196     {
197         XFree( pVInfo );
198     }
199
200     // Create a graphics context that doesn't generate GraphicsExpose events
201     if( m_pDisplay )
202     {
203         XGCValues xgcvalues;
204         xgcvalues.graphics_exposures = False;
205         m_gc = XCreateGC( m_pDisplay, root, GCGraphicsExposures, &xgcvalues );
206
207         // Create a parent window to have a single task in the task bar
208         XSetWindowAttributes attr;
209         m_mainWindow = XCreateWindow( m_pDisplay, root, 0, 0, 1, 1, 0, 0,
210                                       InputOutput, CopyFromParent, 0, &attr );
211
212         // initialize WM_CLASS
213         XClassHint classhint;
214         classhint.res_name = (char*) "vlc";
215         classhint.res_class = (char*) "Vlc";
216         XSetClassHint( m_pDisplay, m_mainWindow, &classhint );
217
218         // Receive WM_DELETE_WINDOW
219         Atom wm_delete = XInternAtom( m_pDisplay, "WM_DELETE_WINDOW", False);
220         XSetWMProtocols( m_pDisplay, m_mainWindow, &wm_delete, 1);
221
222         // Changing decorations
223         struct {
224             unsigned long flags;
225             unsigned long functions;
226             unsigned long decorations;
227             long input_mode;
228             unsigned long status;
229         } motifWmHints;
230         Atom hints_atom = XInternAtom( m_pDisplay, "_MOTIF_WM_HINTS", False );
231         motifWmHints.flags = 2;    // MWM_HINTS_DECORATIONS;
232         motifWmHints.decorations = 0;
233         XChangeProperty( m_pDisplay, m_mainWindow, hints_atom, hints_atom, 32,
234                          PropModeReplace, (unsigned char *)&motifWmHints,
235                          sizeof( motifWmHints ) / sizeof( long ) );
236
237         // Change the window title
238         XStoreName( m_pDisplay, m_mainWindow, "VLC Media Player" );
239
240         // Add an icon
241         Pixmap icon_pixmap, shape_pixmap;
242         int ret = XpmCreatePixmapFromData( m_pDisplay, root, (char**)vlc_xpm,
243                                            &icon_pixmap, &shape_pixmap, NULL );
244         if( ret == XpmSuccess )
245         {
246             XWMHints *wm = XAllocWMHints();
247             if( wm )
248             {
249                 wm->icon_pixmap = icon_pixmap;
250                 wm->icon_mask = shape_pixmap;
251                 wm->flags = (IconPixmapHint|IconMaskHint);
252
253                 XSetWMHints( m_pDisplay, m_mainWindow, wm );
254                 XFree( wm );
255             }
256         }
257         else
258         {
259             msg_Err( getIntf(), "icon failed to be loaded (err=%i)", ret );
260         }
261
262         // Receive map notify events
263         XSelectInput( m_pDisplay, m_mainWindow, StructureNotifyMask );
264
265         // Set an empty mask for the window
266         Region mask = XCreateRegion();
267         XShapeCombineRegion( m_pDisplay, m_mainWindow, ShapeBounding, 0, 0,
268                              mask, ShapeSet );
269         XDestroyRegion( mask );
270
271         XMapWindow( m_pDisplay, m_mainWindow);
272
273         // Move it outside the screen to avoid seeing it in workspace selector
274         XMoveWindow( m_pDisplay, m_mainWindow, -10, -10 );
275
276         // test EWMH capabilities
277         testEWMH();
278
279         // Force _NET_WM_PID whatever the WM _NET_SUPPORTED says
280         m_net_wm_pid = XInternAtom( m_pDisplay, "_NET_WM_PID" , False );
281     }
282 }
283
284
285 X11Display::~X11Display()
286 {
287     if( m_mainWindow ) XDestroyWindow( m_pDisplay, m_mainWindow );
288     if( m_gc )         XFreeGC( m_pDisplay, m_gc );
289     if( m_colormap )   XFreeColormap( m_pDisplay, m_colormap );
290     if( m_pDisplay )   XCloseDisplay( m_pDisplay );
291 }
292
293
294 void X11Display::testEWMH()
295 {
296     int i_ret, i_format;
297     unsigned long i_items, i_bytesafter;
298     union { Atom *p_atom; unsigned char *p_char; } p_args;
299     p_args.p_atom = NULL;
300
301     m_net_wm_supported = XInternAtom( m_pDisplay, "_NET_SUPPORTED", False );
302     msg_Dbg( getIntf(), "EWMH: supported %d", m_net_wm_supported != None ? 1 : 0 );
303     if( m_net_wm_supported == None )
304         return;
305
306     i_ret = XGetWindowProperty( m_pDisplay, DefaultRootWindow( m_pDisplay ),
307                                 m_net_wm_supported,
308                                 0, 16384, False, AnyPropertyType,
309                                 &m_net_wm_supported,
310                                 &i_format, &i_items, &i_bytesafter,
311                                 (unsigned char **)&p_args );
312
313     if( i_ret != Success || i_items == 0 )
314         return; /* Not supported */
315
316 #define TEST_EWMH( name, value ) \
317 {\
318     name = XInternAtom( m_pDisplay, value, False );\
319     int i;\
320     for( i = 0; i < i_items; i++ )\
321     {\
322         if( p_args.p_atom[i] == name ) break;\
323     }\
324     if( i == i_items )\
325     {\
326         msg_Dbg( getIntf(), "%s support: no", value );\
327         name = None;\
328     }\
329     else\
330         msg_Dbg( getIntf(), "%s support: yes", value );\
331 }
332
333     TEST_EWMH( m_net_wm_state, "_NET_WM_STATE" )
334     TEST_EWMH( m_net_wm_state_fullscreen, "_NET_WM_STATE_FULLSCREEN" )
335     TEST_EWMH( m_net_wm_stays_on_top, "_NET_WM_STATE_STAYS_ON_TOP" )
336     TEST_EWMH( m_net_wm_state_above, "_NET_WM_STATE_ABOVE" )
337
338     TEST_EWMH( m_net_wm_window_opacity, "_NET_WM_WINDOW_OPACITY" )
339
340     TEST_EWMH( m_net_wm_pid, "_NET_WM_PID" )
341
342 #undef TEST_EWMH
343
344     XFree( p_args.p_atom );
345
346 }
347
348
349 void X11Display::getShifts( uint32_t mask, int &rLeftShift, int &rRightShift )
350 {
351     for( rLeftShift = 0; (rLeftShift < 32) && !(mask & 1); rLeftShift++ )
352     {
353         mask >>= 1;
354     }
355     for( rRightShift = 8; (mask & 1) ; rRightShift--)
356     {
357         mask >>= 1;
358     }
359     if( rRightShift < 0 )
360     {
361         rLeftShift -= rRightShift;
362         rRightShift = 0;
363     }
364 }
365
366
367 void X11Display::blendPixel8( uint8_t *pPixel, uint8_t r, uint8_t g,
368                               uint8_t b, uint8_t a ) const
369 {
370     *pPixel = 255 - blendPixel<uint8_t>(255 - *pPixel,r,g,b,a);
371 }
372
373
374 void X11Display::blendPixel16MSB( uint8_t *pPixel, uint8_t r, uint8_t g,
375                                   uint8_t b, uint8_t a ) const
376 {
377     uint16_t value = pPixel[1] | pPixel[0] << 8;
378
379     value = blendPixel<uint16_t>(value,r,g,b,a);
380
381     pPixel[1] = value; value >>= 8;
382     pPixel[0] = value;
383 }
384
385
386 void X11Display::blendPixel16LSB( uint8_t *pPixel, uint8_t r, uint8_t g,
387                                   uint8_t b, uint8_t a ) const
388 {
389     uint16_t value = pPixel[0] | pPixel[1] << 8;
390
391     value = blendPixel<uint16_t>(value,r,g,b,a);
392
393     pPixel[0] = value; value >>= 8;
394     pPixel[1] = value;
395 }
396
397
398 void X11Display::blendPixel32MSB( uint8_t *pPixel, uint8_t r, uint8_t g,
399                                   uint8_t b, uint8_t a ) const
400 {
401     uint32_t value = pPixel[3] | pPixel[2] << 8 |
402                      pPixel[1] << 16 | pPixel[0] << 24;
403
404     value = blendPixel<uint32_t>(value,r,g,b,a);
405
406     pPixel[3] = value; value >>= 8;
407     pPixel[2] = value; value >>= 8;
408     pPixel[1] = value; value >>= 8;
409     pPixel[0] = value;
410 }
411
412
413 void X11Display::blendPixel32LSB( uint8_t *pPixel, uint8_t r, uint8_t g,
414                                   uint8_t b, uint8_t a ) const
415 {
416     uint32_t value = pPixel[0] | pPixel[1] << 8 |
417                      pPixel[2] << 16 | pPixel[3] << 24;
418
419     value = blendPixel<uint32_t>(value,r,g,b,a);
420
421     pPixel[0] = value; value >>= 8;
422     pPixel[1] = value; value >>= 8;
423     pPixel[2] = value; value >>= 8;
424     pPixel[3] = value;
425 }
426
427
428 void X11Display::putPixel8( uint8_t *pPixel, uint8_t r, uint8_t g,
429                             uint8_t b, uint8_t a ) const
430 {
431     *pPixel = 255 - putPixel<uint8_t>(r,g,b);
432 }
433
434
435 void X11Display::putPixel16MSB( uint8_t *pPixel, uint8_t r, uint8_t g,
436                                 uint8_t b, uint8_t a ) const
437 {
438     uint16_t value = putPixel<uint16_t>(r, g, b);
439
440     pPixel[1] = value; value >>= 8;
441     pPixel[0] = value;
442 }
443
444
445 void X11Display::putPixel16LSB( uint8_t *pPixel, uint8_t r, uint8_t g,
446                                 uint8_t b, uint8_t a ) const
447 {
448     uint16_t value = putPixel<uint16_t>(r,g,b);
449     pPixel[0] = value; value >>= 8;
450     pPixel[1] = value;
451 }
452
453
454 void X11Display::putPixel32MSB( uint8_t *pPixel, uint8_t r, uint8_t g,
455                                 uint8_t b, uint8_t a ) const
456 {
457     uint32_t value = putPixel<uint32_t>(r,g,b);
458
459     pPixel[3] = value; value >>= 8;
460     pPixel[2] = value; value >>= 8;
461     pPixel[1] = value; value >>= 8;
462     pPixel[0] = value;
463 }
464
465
466 void X11Display::putPixel32LSB( uint8_t *pPixel, uint8_t r, uint8_t g,
467                                 uint8_t b, uint8_t a ) const
468 {
469     uint32_t value = putPixel<uint32_t>(r,g,b);
470
471     pPixel[0] = value; value >>= 8;
472     pPixel[1] = value; value >>= 8;
473     pPixel[2] = value; value >>= 8;
474     pPixel[3] = value;
475 }
476
477
478 unsigned long X11Display::getPixelValue( uint8_t r, uint8_t g, uint8_t b )
479     const
480 {
481     unsigned long value = putPixel<unsigned long>(r,g,b);
482
483     return m_pixelSize==1 ? 255 - value : value;
484 }
485
486 #endif