1 /********************************************************************************
2 * *
3 * C u r s o r - O b j e c t *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1997,2006 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU Lesser General Public *
10 * License as published by the Free Software Foundation; either *
11 * version 2.1 of the License, or (at your option) any later version. *
12 * *
13 * This library is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16 * Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public *
19 * License along with this library; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
21 *********************************************************************************
22 * $Id: FXCursor.cpp,v 1.62.2.1 2006/06/09 00:50:16 fox Exp $ *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "FXHash.h"
28 #include "FXThread.h"
29 #include "FXStream.h"
30 #include "FXString.h"
31 #include "FXSize.h"
32 #include "FXPoint.h"
33 #include "FXRectangle.h"
34 #include "FXSettings.h"
35 #include "FXRegistry.h"
36 #include "FXApp.h"
37 #include "FXId.h"
38 #include "FXVisual.h"
39 #include "FXCursor.h"
40 #include "FXException.h"
41
42
43 /*
44 Notes:
45 - Cursor size should be less than or equal to 32x32; limitation in Windows!
46 - Need standard glyph for "invisible" cursor.
47 - Keep hotx and hoty INSIDE the cursor glyph!!
48 - Thanks Niall Douglas <s_sourceforge@nedprod.com> for the changes for
49 alpha-blended cursors.
50 */
51
52 #define DISPLAY(app) ((Display*)((app)->display))
53 #define DARKCOLOR(r,g,b) (((r)+(g)+(b))<382)
54 #define CURSOR_MASK (255)
55
56
57 using namespace FX;
58
59 /*******************************************************************************/
60
61 namespace FX {
62
63 extern bool fxloadXBM(FXColor*& data,const FXuchar *pixels,const FXuchar *mask,FXint width,FXint height);
64
65
66 // Standard colors
67 const FXColor white=FXRGBA(255,255,255,255);
68 const FXColor black=FXRGBA(0,0,0,255);
69
70
71 // Object implementation
72 FXIMPLEMENT(FXCursor,FXId,NULL,0)
73
74
75 // Deserialization
FXCursor()76 FXCursor::FXCursor(){
77 data=NULL;
78 width=0;
79 height=0;
80 hotx=0;
81 hoty=0;
82 options=CURSOR_ARROW;
83 }
84
85
86 // Make stock cursor
FXCursor(FXApp * a,FXStockCursor curid)87 FXCursor::FXCursor(FXApp* a,FXStockCursor curid):FXId(a){
88 FXTRACE((100,"FXCursor::FXCursor %p\n",this));
89 data=NULL;
90 width=0;
91 height=0;
92 hotx=0;
93 hoty=0;
94 options=curid;
95 }
96
97
98 // Make cursor from source and mask
FXCursor(FXApp * a,const FXuchar * src,const FXuchar * msk,FXint w,FXint h,FXint hx,FXint hy)99 FXCursor::FXCursor(FXApp* a,const FXuchar* src,const FXuchar* msk,FXint w,FXint h,FXint hx,FXint hy):FXId(a){
100 FXTRACE((100,"FXCursor::FXCursor %p\n",this));
101 fxloadXBM(data,src,msk,w,h);
102 width=w;
103 height=h;
104 hotx=FXCLAMP(0,hx,width-1);
105 hoty=FXCLAMP(0,hy,height-1);
106 options=CURSOR_OWNED;
107 }
108
109
110 // Make cursor from FXColor pixels
FXCursor(FXApp * a,const FXColor * pix,FXint w,FXint h,FXint hx,FXint hy)111 FXCursor::FXCursor(FXApp* a,const FXColor *pix,FXint w,FXint h,FXint hx,FXint hy):FXId(a){
112 FXTRACE((100,"FXCursor::FXCursor %p\n",this));
113 data=(FXColor*)pix;
114 width=w;
115 height=h;
116 hotx=FXCLAMP(0,hx,width-1);
117 hoty=FXCLAMP(0,hy,height-1);
118 options=0;
119 }
120
121
122 // Return TRUE if color cursor
isColor() const123 bool FXCursor::isColor() const {
124 register FXint i;
125 if(data){
126 for(i=width*height-1; 0<=i; i--){
127 if(data[i]!=black && data[i]!=white && FXALPHAVAL(data[i])!=0) return true;
128 }
129 }
130 return false;
131 }
132
133
134 #ifdef WIN32
135
supportsColorCursors()136 static bool supportsColorCursors(){
137
138 // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
139 // If that fails, try using the OSVERSIONINFO structure.
140 #if defined (__WATCOMC__) || (__DMC__)
141 OSVERSIONINFO osvi={sizeof(OSVERSIONINFO)};
142 #else
143 OSVERSIONINFOEX osvi={sizeof(OSVERSIONINFOEX)};
144 #endif
145 if(!GetVersionEx((OSVERSIONINFO*)&osvi)){
146
147 // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
148 osvi.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
149 if(!GetVersionEx((OSVERSIONINFO*)&osvi)){
150 return false; // should not happen
151 }
152 }
153 if(osvi.dwPlatformId==VER_PLATFORM_WIN32_NT){
154 if(osvi.dwMajorVersion==5 && osvi.dwMinorVersion>=0 || osvi.dwMajorVersion>5){
155 return true;
156 }
157 }
158
159 return false;
160 }
161
162
163 #endif
164
165 // Create cursor
create()166 void FXCursor::create(){
167 if(!xid){
168 if(getApp()->isInitialized()){
169 FXTRACE((100,"%s::create %p\n",getClassName(),this));
170
171 #ifndef WIN32 // X11
172
173 // Mapping to standard X11 cursors
174 const FXuint stock[]={XC_left_ptr,XC_left_ptr,XC_right_ptr,XC_xterm,XC_watch,XC_crosshair,XC_sb_h_double_arrow,XC_sb_v_double_arrow,XC_fleur};
175
176 // Building stock cursor
177 if(options&CURSOR_MASK){
178 FXTRACE((100,"%s::create: stock cursor\n",getClassName()));
179 xid=XCreateFontCursor(DISPLAY(getApp()),stock[options&CURSOR_MASK]);
180 }
181
182 // Building custom cursor
183 else{
184
185 // Should have data
186 if(!data){ fxerror("%s::create: cursor needs pixel data.\n",getClassName()); }
187
188 // Let's hope it's the correct size!
189 if(width>32 || height>32){ fxerror("%s::create: cursor exceeds maximum size of 32x32 pixels\n",getClassName()); }
190
191 // We have support for color cursors and its a color cursor
192 #ifdef HAVE_XCURSOR_H
193 if(isColor() && XcursorSupportsARGB(DISPLAY(getApp()))){
194 register FXuchar *src,*dst,*end; XcursorImage *image;
195 FXTRACE((100,"%s::create: custom color %dx%d cursor\n",getClassName(),width,height));
196 image=XcursorImageCreate(width,height);
197 image->xhot=hotx;
198 image->yhot=hoty;
199 dst=(FXuchar*)image->pixels;
200 src=(FXuchar*)data;
201 end=src+width*height*4;
202 do{
203 #ifndef __APPLE__
204 dst[0]=src[2]; // B
205 dst[1]=src[1]; // G
206 dst[2]=src[0]; // R
207 dst[3]=src[3]; // A
208 #else
209 // A bug in Apple's X11 implementation has alpha on
210 // the wrong end and BGR wrong way round
211 dst[0]=src[3]; // A
212 dst[3]=src[2]; // B
213 dst[2]=src[1]; // G
214 dst[1]=src[0]; // R
215 #endif
216 dst+=4;
217 src+=4;
218 }
219 while(src<end);
220 xid=XcursorImageLoadCursor(DISPLAY(getApp()),image);
221 XcursorImageDestroy(image);
222 }
223
224 // No support for color cursor or simple black/white cursor
225 else{
226 #endif
227 FXuchar shapebits[128],maskbits[128]; XColor color[2]; Pixmap srcpix,mskpix;
228 register FXint srcoffset,dstoffset,dstbytes,i,j;
229 FXTRACE((100,"%s::create: custom b/w %dx%d cursor\n",getClassName(),width,height));
230 color[0].pixel=BlackPixel(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp())));
231 color[1].pixel=WhitePixel(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp())));
232 color[0].flags=DoRed|DoGreen|DoBlue;
233 color[1].flags=DoRed|DoGreen|DoBlue;
234 XQueryColors(DISPLAY(getApp()),DefaultColormap(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp()))),color,2);
235 memset(shapebits,0,sizeof(shapebits));
236 memset(maskbits,0,sizeof(maskbits));
237 dstbytes=(width+7)/8;
238 srcoffset=dstoffset=0;
239 for(j=0; j<height; j++){
240 for(i=0; i<width; i++){
241 if(((FXuchar*)(data+srcoffset+i))[3]>=128){
242 maskbits[dstoffset+(i>>3)]|=(1<<(i&7));
243 if(DARKCOLOR(((FXuchar*)(data+srcoffset+i))[0],((FXuchar*)(data+srcoffset+i))[1],((FXuchar*)(data+srcoffset+i))[2])) shapebits[dstoffset+(i>>3)]|=(1<<(i&7));
244 }
245 }
246 srcoffset+=width;
247 dstoffset+=dstbytes;
248 }
249 srcpix=XCreateBitmapFromData(DISPLAY(getApp()),XDefaultRootWindow(DISPLAY(getApp())),(char*)shapebits,width,height);
250 if(!srcpix){ throw FXImageException("unable to create cursor"); }
251 mskpix=XCreateBitmapFromData(DISPLAY(getApp()),XDefaultRootWindow(DISPLAY(getApp())),(char*)maskbits,width,height);
252 if(!mskpix){ throw FXImageException("unable to create cursor"); }
253 xid=XCreatePixmapCursor(DISPLAY(getApp()),srcpix,mskpix,&color[0],&color[1],hotx,hoty);
254 XFreePixmap(DISPLAY(getApp()),srcpix);
255 XFreePixmap(DISPLAY(getApp()),mskpix);
256 #ifdef HAVE_XCURSOR_H
257 }
258 #endif
259 }
260
261 #else // WIN32
262
263 // Mapping to standard WIN32 cursors
264 const LPCTSTR stock[]={IDC_ARROW,IDC_ARROW,IDC_ARROW,IDC_IBEAM,IDC_WAIT,IDC_CROSS,IDC_SIZENS,IDC_SIZEWE,IDC_SIZEALL};
265
266 // Building stock cursor
267 if(options&CURSOR_MASK){
268 FXTRACE((100,"%s::create: stock cursor\n",getClassName()));
269 xid=LoadCursor(NULL,stock[options&CURSOR_MASK]);
270 }
271
272 // Building custom cursor
273 else{
274
275 // Should have data
276 if(!data){ fxerror("%s::create: cursor needs pixel data.\n",getClassName()); }
277
278 // Let's hope it's the correct size!
279 if(width>32 || height>32){ fxerror("%s::create: cursor exceeds maximum size of 32x32 pixels\n",getClassName()); }
280
281 FXASSERT(GetSystemMetrics(SM_CXCURSOR)==32);
282 FXASSERT(GetSystemMetrics(SM_CYCURSOR)==32);
283
284 // We have support for color cursors and its a color cursor
285 if(isColor() && supportsColorCursors()){
286 const BITMAPV4HEADER bi={sizeof(BITMAPV4HEADER),32,-32,1,32,BI_BITFIELDS,0,0,0,0,0,0x00FF0000,0x0000FF00,0x000000FF,0xFF000000,0,{{0,0,0},{0,0,0},{0,0,0}},0,0,0};
287 HBITMAP img,mask;
288 ICONINFO ii;
289 FXTRACE((100,"%s::create: custom color %dx%d cursor\n",getClassName(),width,height));
290
291 // Make a DIB
292 void *imgdata=0;
293 HDC hdc=GetDC(NULL);
294 img=CreateDIBSection(hdc,(BITMAPINFO*)&bi,DIB_RGB_COLORS,&imgdata,NULL,0);
295 ReleaseDC(NULL,hdc);
296 if(!img){ throw FXImageException("unable to create cursor"); }
297
298 // Fill in data
299 FXuint *imgptr=(FXuint*)imgdata;
300 FXColor *srcimgptr=data;
301 for(int y=0; y<height; y++){
302 for(int x=0; x<width; x++){
303 FXColor col=*srcimgptr++;
304 *imgptr++=(FXALPHAVAL(col)<<24)|(FXREDVAL(col)<<16)|(FXGREENVAL(col)<<8)|(FXBLUEVAL(col));
305 }
306 for(int fill=width; fill<32; fill++){
307 *imgptr++=0;
308 }
309 }
310 if(height<32) memset(imgptr,0,(32-height)*32);
311
312 // Strawman mask bitmap
313 mask=CreateBitmap(32,32,1,1,NULL);
314 if(!mask){ throw FXImageException("unable to create cursor"); }
315
316 // Create cursor
317 ii.fIcon=FALSE;
318 ii.xHotspot=hotx;
319 ii.yHotspot=hoty;
320 ii.hbmMask=mask;
321 ii.hbmColor=img;
322 xid=CreateIconIndirect(&ii);
323
324 // No longer needed
325 DeleteObject(mask);
326 DeleteObject(img);
327 }
328
329 // No support for color cursor or simple black/white cursor
330 else{
331 FXint i,j,srcoffset,dstoffset; FXuchar tmpxor[128],tmpand[128];
332 FXTRACE((100,"%s::create: custom b/w %dx%d cursor\n",getClassName(),width,height));
333 srcoffset=dstoffset=0;
334 memset(tmpand,0xff,sizeof(tmpand));
335 memset(tmpxor,0,sizeof(tmpxor));
336 for(j=0; j<height; j++){
337 for(i=0; i<width; i++){
338 if(((FXuchar*)(data+srcoffset+i))[3]>=128){
339 tmpand[dstoffset+(i>>3)]&=~(128>>(i&7));
340 if(!DARKCOLOR(((FXuchar*)(data+srcoffset+i))[0],((FXuchar*)(data+srcoffset+i))[1],((FXuchar*)(data+srcoffset+i))[2])){
341 tmpxor[dstoffset+(i>>3)]|=(128>>(i&7));
342 }
343 }
344 }
345 srcoffset+=width;
346 dstoffset+=4;
347 }
348 xid=CreateCursor((HINSTANCE)(getApp()->display),hotx,hoty,32,32,tmpand,tmpxor);
349 }
350 }
351
352 #endif
353
354 // Were we successful?
355 if(!xid){ throw FXImageException("unable to create cursor"); }
356
357 // Release pixel buffer
358 if(!(options&CURSOR_KEEP)) release();
359 }
360 }
361 }
362
363
364 // Detach cursor
detach()365 void FXCursor::detach(){
366 if(xid){
367 FXTRACE((100,"%s::detach %p\n",getClassName(),this));
368 xid=0;
369 }
370 }
371
372
373 // Release pixels buffer if it was owned
release()374 void FXCursor::release(){
375 if(options&CURSOR_OWNED){
376 options&=~CURSOR_OWNED;
377 FXFREE(&data);
378 }
379 data=NULL;
380 }
381
382
383 // Destroy cursor
destroy()384 void FXCursor::destroy(){
385 if(xid){
386 if(getApp()->isInitialized()){
387 FXTRACE((100,"%s::destroy %p\n",getClassName(),this));
388 #ifndef WIN32
389
390 // Delete cursor
391 XFreeCursor(DISPLAY(getApp()),xid);
392 #else
393
394 // Delete cursor
395 DestroyCursor((HCURSOR)xid);
396
397 #endif
398 }
399 xid=0;
400 }
401 }
402
403
404 // Save pixel data only
savePixels(FXStream & store) const405 bool FXCursor::savePixels(FXStream& store) const {
406 FXuint size=width*height;
407 store.save(data,size);
408 return true;
409 }
410
411
412 // Load pixel data only
loadPixels(FXStream & store)413 bool FXCursor::loadPixels(FXStream& store){
414 FXuint size=width*height;
415 if(options&CURSOR_OWNED){FXFREE(&data);}
416 if(!FXMALLOC(&data,FXColor,size)) return false;
417 store.load(data,size);
418 options|=CURSOR_OWNED;
419 return true;
420 }
421
422
423 // Save cursor to stream
save(FXStream & store) const424 void FXCursor::save(FXStream& store) const {
425 FXuchar haspixels=(data!=NULL);
426 FXId::save(store);
427 store << width << height << hotx << hoty;
428 store << options;
429 store << haspixels;
430 if(haspixels) savePixels(store);
431 }
432
433
434 // Load cursor from stream
load(FXStream & store)435 void FXCursor::load(FXStream& store){
436 FXuchar haspixels;
437 FXId::load(store);
438 store >> width >> height >> hotx >> hoty;
439 store >> options;
440 store >> haspixels;
441 if(haspixels) loadPixels(store);
442 }
443
444
445 // Clean up
~FXCursor()446 FXCursor::~FXCursor(){
447 FXTRACE((100,"FXCursor::~FXCursor %p\n",this));
448 destroy();
449 if(options&CURSOR_OWNED){FXFREE(&data);}
450 data=(FXColor *)-1L;
451 }
452
453 }
454