如何使用XLib和Xcursors在Linux中加载Windows游标(.cur)?

时间:2014-09-27 20:13:18

标签: linux ubuntu xlib glfw

我正在尝试通过 Xcursor 库加载.cur文件,但我无法使其正常工作。我在virtualbox上使用linux (Bodhi + Lubuntu)。这是我的代码:

my includes

// GLFW
#define GLFW_DLL       // use the GLFW .dll
#include "gl/GLFW/glfw3.h"

...

#if defined(__linux__)
   // GLFW native expoose
   #define GLFW_EXPOSE_NATIVE_X11        // expose X11 and GLX context (linux)
   #define GLFW_EXPOSE_NATIVE_GLX
   #include "gl/GLFW/glfw3native.h"      // for low-level expose
   // X Window
   #include <X11/X.h> 
   #include <X11/Xlib.h> 
   #include <X11/Xutil.h>
   #include <X11/Xcursor/Xcursor.h>
#endif
程序中的

,在创建GLFW窗口之后

#if defined(__linux__)
   Display* dpy = glfwGetX11Display();
   Window xwindow = glfwGetX11Window(window);
   Cursor crs = XcursorFilenameLoadCursor(dpy,"somefolder/default.cur");
   XDefineCursor(dpy,xwindow,crs);
#endif

...

#if defined(__linux__)
   XFreeCursor(dpy,crs);
#endif

有任何明显错误吗?我无法真正找到有关Xcursor的消息来源,而且我接近切换到典型的OpenGL纹理并克服它。

注意:我已经禁用了Virtualbox集成鼠标,因此鼠标完全在客户机上,我全屏运行。

1 个答案:

答案 0 :(得分:1)

由于我解决了我的问题,我认为发布我的结果是一个好主意,希望能帮助某人试图让他的应用程序更具跨平台性。 我的代码如下,解释遵循代码。

<强> ESSENTIAL

  • X11开发标题包(等等 libx11-dev
  • Xcursor development headers包(等等 libxcursor-dev
  • 便携式<stdint.h>标头,如this一个。

<强> CODE

...
// to store colors
struct COLOR {
   uint8_t r,g,b,a;
   COLOR() {
      this->r = this->g = this->b = this->a = 0;
   }
   COLOR(uint8_t r,uint8_t g,uint8_t b) {
      this->r = r;
      this->g = g;
      this->b = b;
      this->a = 255;
   }
   bool operator == (COLOR c) const {
      return (r == c.r && g == c.g && b == c.b);
   }
};

size_t get_bit(int32_t number,int8_t position) {
   size_t bitmask = 1 << position;
   return (number & bitmask) ? 1 : 0;
}

....
// load cursor
#if defined(_WIN32)
   // etc. use standard WinApi code, (HCURSOR)LoadImage(..) and such
#endif
#if defined(__linux__)

  Display* display = XOpenDisplay(NULL);

  string filename = "mycursor.cur";    // <-- add your .cur cursor here
  char buf[4];
  FILE* fp = fopen(filename.c_str(),"rb");
  fread(buf,1,2,fp);    // reserved. always 0
  fread(buf,1,2,fp);    // image type; we're only interested for .cur (=2)
  int16_t imagetype = buf[0] | (buf[1]<<8);
  if (imagetype!=2) {
     // error:  file is not a valid .cur file
     return;
  }
  fread(buf,1,2,fp);    // number of images
  // we're only interested in the first image
  fread(buf,1,1,fp);    // width  (0 to 255; 0=means 256 width)
  int8_t width = (buf[0]==0 ? 256 : buf[0]);
  fread(buf,1,1,fp);    // height (0 to 255; 0=means 256 height)
  int8_t height = (buf[0]==0 ? 256 : buf[0]);
  fread(buf,1,1,fp);    // number of colors in palette (0 for no palette)
  fread(buf,1,1,fp);    // reserved. should be 0
  fread(buf,1,2,fp);    // hotspot x
  int16_t hotspot_x = buf[0] | (buf[1]<<8);
  fread(buf,1,2,fp);    // hotspot y
  int16_t hotspot_y = buf[0] | (buf[1]<<8);
  fread(buf,1,4,fp);    // image data in bytes
  fread(buf,1,4,fp);    // offset to image data

  // Now we need to verify if image in .cur is BMP or PNG (Vista+)
  // We can't just check 'magic' since if it's BMP, the header will be missing (PNG is whole)
  // So we search for PNG magic; if doesnt exist, we have a BMP!
  // NOTE:  for simplicity we go only for BMP for the moment. 
  //        So just check if 'biSize' is 40 (Windows NT & 3.1x or later)

  // BITMAPINFOHEADER 
  fread(buf,1,4,fp);    // biSize
  int32_t biSize = (buf[0]&0xff) | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
  if (biSize!=40) {
     // error:  file does not contain valid BMP data;
     return;
  }
  fread(buf,1,4,fp);    // biWidth
  int32_t biWidth = (buf[0]&0xff) | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
  fread(buf,1,4,fp);    // biHeight (if positive => bottom-up, if negative => up-bottom)
  int32_t biHeight = (buf[0]&0xff) | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
  fread(buf,1,2,fp);    // biPlanes
  fread(buf,1,2,fp);    // biBitCount         
  int16_t biBitCount = (buf[0]&0xff) | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
  if (biBitCount!=24 && biBitCount!=32) {
     // error:  only 24/32 bits supported;
     return;
  }
  fread(buf,1,4,fp);    // biCompression
  int32_t biCompression = (buf[0]&0xff) | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
  // we want only uncompressed BMP data
  if (biCompression!=0 /*BI_RGB*/ ) {
     // error:  file is compressed; only uncompressed BMP is supported;
     return;
  }
  fread(buf,1,4,fp);    // biSizeImage
  fread(buf,1,4,fp);    // biXPelsPerMeter
  fread(buf,1,4,fp);    // biYPelsPerMeter
  fread(buf,1,4,fp);    // biClrUsed
  fread(buf,1,4,fp);    // biClrImportant

  // DECODE IMAGE
  uint8_t origin = (biHeight>0 ? 0 : 1);    // 0=bottom-up, 1=up-bottom
  // there are cases where BMP sizes are NOT the same with cursor; we use the cursor ones
  biWidth = width;
  biHeight = height;

  COLOR* pixels = new COLOR[biWidth * biHeight];
  for(int32_t y=0;y<biHeight;y++) {
     for(int32_t x=0;x<biWidth;x++) {
        uint32_t offset = ((origin==1?y:biHeight-1-y)*biWidth)+x;
        // read pixels by number of bits
        switch(biBitCount) {
           // 24-bit
           case 24:
              fread(buf,1,3,fp); 
              pixels[offset] = COLOR(buf[0],buf[1],buf[2]);
           break;
           // 32-bit
           case 32:
              fread(buf,1,4,fp);
              pixels[offset] = COLOR(buf[0],buf[1],buf[2]);
              pixels[offset].a = buf[3];
           break;
        }
     }
  }

  // read mask
  // mask is 1-bit-per-pixel for describing the cursor bitmap's opaque and transparent pixels.
  // so for 1 pixel we need 1 bit, for etc. 32 pixels width, we need 32*1 bits (or 4 bytes per line)
  // so for etc. 32 pixels height we need 4*32 = 128 bytes or [mask bytes per line * height]
  uint16_t mask_bytes_per_line = biWidth / 8;
  uint16_t mask_bytes = mask_bytes_per_line * biHeight;
  char* mask = new char[mask_bytes];

  fread(mask,1,mask_bytes,fp);
  fclose(fp);

  // reverse every [mask_bytes_per_line] bytes; (etc. for 4 bytes per line do:  0,1,2,3 => 3,2,1,0) -> you really need to ask Microsoft for this 8)
  char rmask[4];
  for(uint16_t x=0;x<mask_bytes;x+=mask_bytes_per_line) {
     for(uint16_t r=0;r<mask_bytes_per_line;r++) {
        rmask[r] = mask[x+mask_bytes_per_line-1-r];
     }
     // copy the reversed line
     for(uint16_t r=0;r<mask_bytes_per_line;r++) {
        mask[x+r] = rmask[r];
     }
  }

  // now extract all bits from mask bytes into new array (equal to width*height)
  vector<uint8_t> bits;
  for(uint16_t x=0;x<mask_bytes;x++) {
     for(uint8_t b=0;b<8;b++) {
        bits.push_back( get_bit(mask[x],b) );
     }
  }
  delete[] mask;
  // reverse vector (?)
  reverse(bits.begin(),bits.end());

  // now the bits contains =1 (transparent) and =0 (opaque) which we use on cursor directly
  XcursorImage* cimg = XcursorImageCreate(biWidth,biHeight);
  cimg->xhot = hotspot_x;
  cimg->yhot = hotspot_y;
  // create the image
  for(int32_t y=0;y<biHeight;y++) {
     for(int32_t x=0;x<biWidth;x++) {
        uint32_t offset = (y*biWidth)+x;
        COLOR pix = pixels[offset];
        cimg->pixels[offset] = ((bits[offset]==1?0:pix.a)<<24) + (pix.r<<16) + (pix.g<<8) + (pix.b);
     }
  }
  // create cursor from image and release the image
  Cursor cursor = XcursorImageLoadCursor(display,cimg);
  XcursorImageDestroy(cimg);

  ...
  // set the cursor
  XDefineCursor(display,yourwindow,cursor);
  XFlush(display);

  ...
  // free cursor
  if (cursor!=None) {
     XFreeCursor(display,cursor);
  }

以上代码采用Windows .cur游标文件并创建Xcursor以在X11窗口系统中使用。当然,我的.cur格式处理存在很多限制,但是可以轻松地对上面的代码添加自己的改进(比如说支持8-bit游标)。上面的代码不仅关注alpha透明度,还关注 32位alpha混合游标