text: /*****************************************************************************
   * x11_display.cpp
   *****************************************************************************
   * Copyright (C) 2003 the VideoLAN team
   * $Id: 4dc86cb861c331621d90c58982a162803a7b6566 $
   *
   * Authors: Cyril Deguet     <asmax@via.ecp.fr>
   *          Olivier Teulière <ipkiss@via.ecp.fr>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License along
   * with this program; if not, write to the Free Software Foundation, Inc.,
   * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
   *****************************************************************************/
  
  #ifdef X11_SKINS
  
  #include <X11/Xlib.h>
  #include <X11/Xutil.h>
  #include <X11/xpm.h>
  #include <X11/extensions/shape.h>
  
  #include "x11_display.hpp"
  #include "../src/logger.hpp"
  
  #include "../share/icons/32x32/vlc.xpm"
  
  template<class type> type X11Display::putPixel(type r, type g, type b) const
  {
      return ( (r >> m_redRightShift)   << m_redLeftShift   ) |
             ( (g >> m_greenRightShift) << m_greenLeftShift ) |
             ( (b >> m_blueRightShift)  << m_blueLeftShift  );
  }
  
  template<class type>
  type X11Display::blendPixel(type v,type r, type g, type b, type a) const
  {
      uint16_t temp;
  
      temp = ((uint8_t)((v >> m_redLeftShift) << m_redRightShift));
      uint8_t red = r + ( temp * (255 - a) ) / 255;
      temp = ((uint8_t)((v >> m_greenLeftShift) << m_greenRightShift));
      uint8_t green = g + ( temp * (255 - a) ) / 255;
      temp = ((uint8_t)((v >> m_blueLeftShift) << m_blueRightShift));
      uint8_t blue = b + ( temp * (255 - a) ) / 255;
  
      return putPixel<type>(red,green,blue);
  }
  
  X11Display::X11Display( intf_thread_t *pIntf ): SkinObject( pIntf ),
      m_mainWindow( 0 ), m_gc( NULL ), m_colormap( 0 )
  {
      // Open a connection to the X Server
      m_pDisplay = XOpenDisplay( NULL );
      if( m_pDisplay == NULL )
      {
          msg_Dbg( pIntf, "Cannot open X display" );
          return;
      }
  
      // Load the XShape extension
      int event, error;
      XShapeQueryExtension( m_pDisplay, &event, &error );
  
      // Get the display parameters
      int screen = DefaultScreen( m_pDisplay );
      int depth = DefaultDepth( m_pDisplay, screen );
      int order = ImageByteOrder( m_pDisplay );
      Window root = DefaultRootWindow( m_pDisplay );
  
      // Template for looking up the XVisualInfo
      XVisualInfo xVInfoTemplate;
      xVInfoTemplate.screen = screen;
      xVInfoTemplate.depth = depth;
  
      XVisualInfo *pVInfo = NULL;
      int vCount = 0;
  
      switch( depth )
      {
      case 8:
          xVInfoTemplate.c_class = DirectColor;
          // Get the DirectColor visual
          pVInfo = XGetVisualInfo( m_pDisplay, VisualScreenMask |
                                   VisualClassMask, &xVInfoTemplate,
                                   &vCount );
          if( pVInfo == NULL )
          {
              msg_Err( getIntf(), "no DirectColor visual available" );
              m_pDisplay = NULL;
              break;
          }
          m_pVisual = pVInfo->visual;
  
          // Compute the color shifts
          getShifts( pVInfo->red_mask, m_redLeftShift, m_redRightShift );
          getShifts( pVInfo->green_mask, m_greenLeftShift,
                     m_greenRightShift );
          getShifts( pVInfo->blue_mask, m_blueLeftShift, m_blueRightShift );
  
          // Create a color map
          m_colormap = XCreateColormap( m_pDisplay, root,
                  DefaultVisual( m_pDisplay, screen ), AllocAll );
  
          // Create the palette
          XColor pColors[255];
          for( uint16_t i = 0; i < 255; i++ )
          {
              // kludge: colors are indexed reversely because color 255 seems
              // to bereserved for black even if we try to set it to white
              pColors[i].pixel = 254-i;
              pColors[i].pad   = 0;
              pColors[i].flags = DoRed | DoGreen | DoBlue;
              pColors[i].red   =
                  (i >> m_redLeftShift) << (m_redRightShift + 8);
              pColors[i].green =
                  (i >> m_greenLeftShift) << (m_greenRightShift + 8);
              pColors[i].blue  =
                  (i >> m_blueLeftShift) << (m_blueRightShift + 8);
          }
          XStoreColors( m_pDisplay, m_colormap, pColors, 255 );
          blendPixelImpl = &X11Display::blendPixel8;
          putPixelImpl = &X11Display::putPixel8;
          m_pixelSize = 1;
          break;
  
      case 15:
      case 16:
      case 24:
      case 32:
          // Get the TrueColor visual
          xVInfoTemplate.c_class = TrueColor;
          pVInfo = XGetVisualInfo( m_pDisplay, VisualScreenMask |
                                   VisualDepthMask | VisualClassMask,
                                   &xVInfoTemplate, &vCount );
          if( pVInfo == NULL )
          {
              msg_Err( getIntf(), "No TrueColor visual for depth %d", depth );
              m_pDisplay = NULL;
              break;
          }
          m_pVisual = pVInfo->visual;
  
          // Compute the color shifts
          getShifts( pVInfo->red_mask,   m_redLeftShift,   m_redRightShift   );
          getShifts( pVInfo->green_mask, m_greenLeftShift, m_greenRightShift );
          getShifts( pVInfo->blue_mask,  m_blueLeftShift,  m_blueRightShift  );
  
          if( depth == 15 || depth == 16 )
          {
              if( order == MSBFirst )
              {
                  blendPixelImpl = &X11Display::blendPixel16MSB;
                  putPixelImpl = &X11Display::putPixel16MSB;
              }
              else
              {
                  blendPixelImpl = &X11Display::blendPixel16LSB;
                  putPixelImpl = &X11Display::putPixel16LSB;
              }
              m_pixelSize = 2;
          }
          else
          {
              if( order == MSBFirst )
              {
                  blendPixelImpl = &X11Display::blendPixel32MSB;
                  putPixelImpl = &X11Display::putPixel32MSB;
              }
              else
              {
                  blendPixelImpl = &X11Display::blendPixel32LSB;
                  putPixelImpl = &X11Display::putPixel32LSB;
              }
              m_pixelSize = 4;
          }
          break;
  
      default:
          msg_Err( getIntf(), "unsupported depth: %d bpp", depth );
          m_pDisplay = NULL;
          break;
      }
  
      // Free the visual info
      if( pVInfo )
      {
          XFree( pVInfo );
      }
  
      // Create a graphics context that doesn't generate GraphicsExpose events
      if( m_pDisplay )
      {
          XGCValues xgcvalues;
          xgcvalues.graphics_exposures = False;
          m_gc = XCreateGC( m_pDisplay, root, GCGraphicsExposures, &xgcvalues );
  
          // Create a parent window to have a single task in the task bar
          XSetWindowAttributes attr;
          m_mainWindow = XCreateWindow( m_pDisplay, root, 0, 0, 1, 1, 0, 0,
                                        InputOutput, CopyFromParent, 0, &attr );
  
          // initialize WM_CLASS
          XClassHint classhint;
          classhint.res_name = (char*) "vlc";
          classhint.res_class = (char*) "Vlc";
          XSetClassHint( m_pDisplay, m_mainWindow, &classhint );
  
          // Receive WM_DELETE_WINDOW
          Atom wm_delete = XInternAtom( m_pDisplay, "WM_DELETE_WINDOW", False);
          XSetWMProtocols( m_pDisplay, m_mainWindow, &wm_delete, 1);
  
          // Changing decorations
          struct {
              unsigned long flags;
              unsigned long functions;
              unsigned long decorations;
              long input_mode;
              unsigned long status;
          } motifWmHints;
          Atom hints_atom = XInternAtom( m_pDisplay, "_MOTIF_WM_HINTS", False );
          motifWmHints.flags = 2;    // MWM_HINTS_DECORATIONS;
          motifWmHints.decorations = 0;
          XChangeProperty( m_pDisplay, m_mainWindow, hints_atom, hints_atom, 32,
                           PropModeReplace, (unsigned char *)&motifWmHints,
                           sizeof( motifWmHints ) / sizeof( long ) );
  
          // Change the window title
          XStoreName( m_pDisplay, m_mainWindow, "VLC Media Player" );
  
          // Add an icon
          Pixmap icon_pixmap, shape_pixmap;
          int ret = XpmCreatePixmapFromData( m_pDisplay, root, (char**)vlc_xpm,
                                             &icon_pixmap, &shape_pixmap, NULL );
          if( ret == XpmSuccess )
          {
              XWMHints *wm = XAllocWMHints();
              if( wm )
              {
                  wm->icon_pixmap = icon_pixmap;
                  wm->icon_mask = shape_pixmap;
                  wm->flags = (IconPixmapHint|IconMaskHint);
  
                  XSetWMHints( m_pDisplay, m_mainWindow, wm );
                  XFree( wm );
              }
          }
          else
          {
              msg_Err( getIntf(), "icon failed to be loaded (err=%i)", ret );
          }
  
          // Receive map notify events
          XSelectInput( m_pDisplay, m_mainWindow, StructureNotifyMask );
  
          // Set an empty mask for the window
          Region mask = XCreateRegion();
          XShapeCombineRegion( m_pDisplay, m_mainWindow, ShapeBounding, 0, 0,
                               mask, ShapeSet );
          XDestroyRegion( mask );
  
          XMapWindow( m_pDisplay, m_mainWindow);
  
          // Move it outside the screen to avoid seeing it in workspace selector
          XMoveWindow( m_pDisplay, m_mainWindow, -10, -10 );
  
          // test EWMH capabilities
          testEWMH();
  
          // Force _NET_WM_PID whatever the WM _NET_SUPPORTED says
          m_net_wm_pid = XInternAtom( m_pDisplay, "_NET_WM_PID" , False );
      }
  }
  
  
  X11Display::~X11Display()
  {
      if( m_mainWindow ) XDestroyWindow( m_pDisplay, m_mainWindow );
      if( m_gc )         XFreeGC( m_pDisplay, m_gc );
      if( m_colormap )   XFreeColormap( m_pDisplay, m_colormap );
      if( m_pDisplay )   XCloseDisplay( m_pDisplay );
  }
  
  
  void X11Display::testEWMH()
  {
      int i_ret, i_format;
      unsigned long i_items, i_bytesafter;
      union { Atom *p_atom; unsigned char *p_char; } p_args;
      p_args.p_atom = NULL;
  
      m_net_wm_supported = XInternAtom( m_pDisplay, "_NET_SUPPORTED", False );
      msg_Dbg( getIntf(), "EWMH: supported %d", m_net_wm_supported != None ? 1 : 0 );
      if( m_net_wm_supported == None )
          return;
  
      i_ret = XGetWindowProperty( m_pDisplay, DefaultRootWindow( m_pDisplay ),
                                  m_net_wm_supported,
                                  0, 16384, False, AnyPropertyType,
                                  &m_net_wm_supported,
                                  &i_format, &i_items, &i_bytesafter,
                                  (unsigned char **)&p_args );
  
      if( i_ret != Success || i_items == 0 )
          return; /* Not supported */
  
  #define TEST_EWMH( name, value ) \
  {\
      name = XInternAtom( m_pDisplay, value, False );\
      unsigned int i;\
      for( i = 0; i < i_items; i++ )\
      {\
          if( p_args.p_atom[i] == name ) break;\
      }\
      if( i == i_items )\
      {\
          msg_Dbg( getIntf(), "%s support: no", value );\
          name = None;\
      }\
      else\
          msg_Dbg( getIntf(), "%s support: yes", value );\
  }
  
      TEST_EWMH( m_net_wm_window_type, "_NET_WM_WINDOW_TYPE" )
      TEST_EWMH( m_net_wm_window_type_normal, "_NET_WM_WINDOW_TYPE_NORMAL" )
  
      TEST_EWMH( m_net_wm_state, "_NET_WM_STATE" )
      TEST_EWMH( m_net_wm_state_fullscreen, "_NET_WM_STATE_FULLSCREEN" )
      TEST_EWMH( m_net_wm_stays_on_top, "_NET_WM_STATE_STAYS_ON_TOP" )
      TEST_EWMH( m_net_wm_state_above, "_NET_WM_STATE_ABOVE" )
  
      TEST_EWMH( m_net_wm_window_opacity, "_NET_WM_WINDOW_OPACITY" )
  
      TEST_EWMH( m_net_wm_pid, "_NET_WM_PID" )
  
  #undef TEST_EWMH
  
      XFree( p_args.p_atom );
  
  }
  
  
  void X11Display::getShifts( uint32_t mask, int &rLeftShift, int &rRightShift )
  {
      for( rLeftShift = 0; (rLeftShift < 32) && !(mask & 1); rLeftShift++ )
      {
          mask >>= 1;
      }
      for( rRightShift = 8; (mask & 1) ; rRightShift--)
      {
          mask >>= 1;
      }
      if( rRightShift < 0 )
      {
          rLeftShift -= rRightShift;
          rRightShift = 0;
      }
  }
  
  
  void X11Display::blendPixel8( uint8_t *pPixel, uint8_t r, uint8_t g,
                                uint8_t b, uint8_t a ) const
  {
      *pPixel = 255 - blendPixel<uint8_t>(255 - *pPixel,r,g,b,a);
  }
  
  
  void X11Display::blendPixel16MSB( uint8_t *pPixel, uint8_t r, uint8_t g,
                                    uint8_t b, uint8_t a ) const
  {
      uint16_t value = pPixel[1] | pPixel[0] << 8;
  
      value = blendPixel<uint16_t>(value,r,g,b,a);
  
      pPixel[1] = value; value >>= 8;
      pPixel[0] = value;
  }
  
  
  void X11Display::blendPixel16LSB( uint8_t *pPixel, uint8_t r, uint8_t g,
                                    uint8_t b, uint8_t a ) const
  {
      uint16_t value = pPixel[0] | pPixel[1] << 8;
  
      value = blendPixel<uint16_t>(value,r,g,b,a);
  
      pPixel[0] = value; value >>= 8;
      pPixel[1] = value;
  }
  
  
  void X11Display::blendPixel32MSB( uint8_t *pPixel, uint8_t r, uint8_t g,
                                    uint8_t b, uint8_t a ) const
  {
      uint32_t value = pPixel[3] | pPixel[2] << 8 |
                       pPixel[1] << 16 | pPixel[0] << 24;
  
      value = blendPixel<uint32_t>(value,r,g,b,a);
  
      pPixel[3] = value; value >>= 8;
      pPixel[2] = value; value >>= 8;
      pPixel[1] = value; value >>= 8;
      pPixel[0] = value;
  }
  
  
  void X11Display::blendPixel32LSB( uint8_t *pPixel, uint8_t r, uint8_t g,
                                    uint8_t b, uint8_t a ) const
  {
      uint32_t value = pPixel[0] | pPixel[1] << 8 |
                       pPixel[2] << 16 | pPixel[3] << 24;
  
      value = blendPixel<uint32_t>(value,r,g,b,a);
  
      pPixel[0] = value; value >>= 8;
      pPixel[1] = value; value >>= 8;
      pPixel[2] = value; value >>= 8;
      pPixel[3] = value;
  }
  
  
  void X11Display::putPixel8( uint8_t *pPixel, uint8_t r, uint8_t g,
                              uint8_t b, uint8_t a ) const
  {
      (void)a;
      *pPixel = 255 - putPixel<uint8_t>(r,g,b);
  }
  
  
  void X11Display::putPixel16MSB( uint8_t *pPixel, uint8_t r, uint8_t g,
                                  uint8_t b, uint8_t a ) const
  {
      (void)a;
      uint16_t value = putPixel<uint16_t>(r, g, b);
  
      pPixel[1] = value; value >>= 8;
      pPixel[0] = value;
  }
  
  
  void X11Display::putPixel16LSB( uint8_t *pPixel, uint8_t r, uint8_t g,
                                  uint8_t b, uint8_t a ) const
  {
      (void)a;
      uint16_t value = putPixel<uint16_t>(r,g,b);
      pPixel[0] = value; value >>= 8;
      pPixel[1] = value;
  }
  
  
  void X11Display::putPixel32MSB( uint8_t *pPixel, uint8_t r, uint8_t g,
                                  uint8_t b, uint8_t a ) const
  {
      (void)a;
      uint32_t value = putPixel<uint32_t>(r,g,b);
  
      pPixel[3] = value; value >>= 8;
      pPixel[2] = value; value >>= 8;
      pPixel[1] = value; value >>= 8;
      pPixel[0] = value;
  }
  
  
  void X11Display::putPixel32LSB( uint8_t *pPixel, uint8_t r, uint8_t g,
                                  uint8_t b, uint8_t a ) const
  {
      (void)a;
      uint32_t value = putPixel<uint32_t>(r,g,b);
  
      pPixel[0] = value; value >>= 8;
      pPixel[1] = value; value >>= 8;
      pPixel[2] = value; value >>= 8;
      pPixel[3] = value;
  }
  
  
  unsigned long X11Display::getPixelValue( uint8_t r, uint8_t g, uint8_t b )
      const
  {
      unsigned long value = putPixel<unsigned long>(r,g,b);
  
      return m_pixelSize==1 ? 255 - value : value;
  }
  
  #endif
  