如何使用XLib和Xcursors在Linux中加载windows游标(.cur)?
我正试图通过库加载如何使用XLib和Xcursors在Linux中加载windows游标(.cur)?,linux,ubuntu,xlib,glfw,Linux,Ubuntu,Xlib,Glfw,我正试图通过库加载.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
.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
有明显的错误吗?我真的找不到有关Xcursor
的源代码,我很快就会切换到典型的OpenGL纹理并克服它
注意:我已经禁用了Virtualbox集成鼠标,所以鼠标完全在来宾计算机上,我全屏运行它。自从我解决了我的问题后,我决定发布我的结果是个好主意,希望能帮助尝试使其应用程序更跨平台的人。 下面是我的代码,下面是解释 必要的
- X11开发标题包(等libx11开发)
- Xcursor开发头包(等libxcursor开发)
- 便携式
头,如一个
...
// 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);
}
。。。
//储存颜色
结构颜色{
uint8_t r,g,b,a;
颜色(){
这个->r=这个->g=这个->b=这个->a=0;
}
颜色(uint8\u t r、uint8\u t g、uint8\u t b){
这个->r=r;
这->g=g;
这->b=b;
这->a=255;
}
布尔运算符==(颜色c)常量{
返回(r==c.r&&g==c.g&&b==c.b);
}
};
大小获取位(int32位,int8位){
size\u t bitmask=1您的代码在这里工作(Archlinux,带有xmonad或Xfce/xfwm和来自的游标)。@Leiaz,谢谢您的评论。我假设您测试的游标是.xbm
(x窗口位图)?从文档中,我无法确定Xcursor是否支持.cur
。感谢您的努力。哦……这可能就是问题所在。文件
说它是一个“X11游标”..cur
似乎是Windows格式。Xcursor文件格式在手册页中定义,我认为不可能直接使用其他格式。有一个实用程序可以从PNG文件创建Xcursor@Leiaz,谢谢。我觉得.cur
与Xcursor兼容,但它们不兼容。甚至.xbm
或.xpm
都没有加载。你的游标也加载了:),尽管我不知道它们是什么或如何创建它们(我是说,规范)唯一可以确定的是,它们的标题是以Xcur..
开头的。将PNG转换为xcursor
我觉得它不是很好,因为我希望能够在Windows/Linux中使用相同的光标,并且不想真正提供热点;也许一旦我发现,从.cur
到xcurso`的一些内部转换可能是一种方式xcursor
格式。我将发布我的结果。Oracle Virtualbox中存在一个问题,无法在OpenGL上下文上显示硬件游标;它们已在其下显示,这是已知的问题(据我所知)已经有一段时间了。这给人的印象是没有加载游标。我刚切换到VMware,它没有受到这种影响。
...
// 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);
}