1 /********************************************************************************
2 * *
3 * C u r s o r - O b j e c t *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1997,2005 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.55.2.1 2005/02/09 06:30:24 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 FXbool 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 FXbool 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 FXbool 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 dst[0]=src[2]; // B
204 dst[1]=src[1]; // G
205 dst[2]=src[0]; // R
206 dst[3]=src[3]; // A
207 dst+=4;
208 src+=4;
209 }
210 while(src<end);
211 xid=XcursorImageLoadCursor(DISPLAY(getApp()),image);
212 XcursorImageDestroy(image);
213 }
214
215 // No support for color cursor or simple black/white cursor
216 else{
217 #endif
218 FXuchar shapebits[128],maskbits[128]; XColor color[2]; Pixmap srcpix,mskpix;
219 register FXint srcoffset,dstoffset,dstbytes,i,j;
220 FXTRACE((100,"%s::create: custom b/w %dx%d cursor\n",getClassName(),width,height));
221 color[0].pixel=BlackPixel(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp())));
222 color[1].pixel=WhitePixel(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp())));
223 color[0].flags=DoRed|DoGreen|DoBlue;
224 color[1].flags=DoRed|DoGreen|DoBlue;
225 XQueryColors(DISPLAY(getApp()),DefaultColormap(DISPLAY(getApp()),DefaultScreen(DISPLAY(getApp()))),color,2);
226 memset(shapebits,0,sizeof(shapebits));
227 memset(maskbits,0,sizeof(maskbits));
228 dstbytes=(width+7)/8;
229 srcoffset=dstoffset=0;
230 for(j=0; j<height; j++){
231 for(i=0; i<width; i++){
232 if(((FXuchar*)(data+srcoffset+i))[3]>=128){
233 maskbits[dstoffset+(i>>3)]|=(1<<(i&7));
234 if(DARKCOLOR(((FXuchar*)(data+srcoffset+i))[0],((FXuchar*)(data+srcoffset+i))[1],((FXuchar*)(data+srcoffset+i))[2])) shapebits[dstoffset+(i>>3)]|=(1<<(i&7));
235 }
236 }
237 srcoffset+=width;
238 dstoffset+=dstbytes;
239 }
240 srcpix=XCreateBitmapFromData(DISPLAY(getApp()),XDefaultRootWindow(DISPLAY(getApp())),(char*)shapebits,width,height);
241 if(!srcpix){ throw FXImageException("unable to create cursor"); }
242 mskpix=XCreateBitmapFromData(DISPLAY(getApp()),XDefaultRootWindow(DISPLAY(getApp())),(char*)maskbits,width,height);
243 if(!mskpix){ throw FXImageException("unable to create cursor"); }
244 xid=XCreatePixmapCursor(DISPLAY(getApp()),srcpix,mskpix,&color[0],&color[1],hotx,hoty);
245 XFreePixmap(DISPLAY(getApp()),srcpix);
246 XFreePixmap(DISPLAY(getApp()),mskpix);
247 #ifdef HAVE_XCURSOR_H
248 }
249 #endif
250 }
251
252 #else // WIN32
253
254 // Mapping to standard WIN32 cursors
255 const LPCTSTR stock[]={IDC_ARROW,IDC_ARROW,IDC_ARROW,IDC_IBEAM,IDC_WAIT,IDC_CROSS,IDC_SIZENS,IDC_SIZEWE,IDC_SIZEALL};
256
257 // Building stock cursor
258 if(options&CURSOR_MASK){
259 FXTRACE((100,"%s::create: stock cursor\n",getClassName()));
260 xid=LoadCursor(NULL,stock[options&CURSOR_MASK]);
261 }
262
263 // Building custom cursor
264 else{
265
266 // Should have data
267 if(!data){ fxerror("%s::create: cursor needs pixel data.\n",getClassName()); }
268
269 // Let's hope it's the correct size!
270 if(width>32 || height>32){ fxerror("%s::create: cursor exceeds maximum size of 32x32 pixels\n",getClassName()); }
271
272 FXASSERT(GetSystemMetrics(SM_CXCURSOR)==32);
273 FXASSERT(GetSystemMetrics(SM_CYCURSOR)==32);
274
275 // We have support for color cursors and its a color cursor
276 if(isColor() && supportsColorCursors()){
277 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};
278 HBITMAP img,mask;
279 ICONINFO ii;
280 FXTRACE((100,"%s::create: custom color %dx%d cursor\n",getClassName(),width,height));
281
282 // Make a DIB
283 void *imgdata=0;
284 HDC hdc=GetDC(NULL);
285 img=CreateDIBSection(hdc,(BITMAPINFO*)&bi,DIB_RGB_COLORS,&imgdata,NULL,0);
286 ReleaseDC(NULL,hdc);
287 if(!img){ throw FXImageException("unable to create cursor"); }
288
289 // Fill in data
290 FXuint *imgptr=(FXuint*)imgdata;
291 FXColor *srcimgptr=data;
292 for(int y=0; y<height; y++){
293 for(int x=0; x<width; x++){
294 FXColor col=*srcimgptr++;
295 *imgptr++=(FXALPHAVAL(col)<<24)|(FXREDVAL(col)<<16)|(FXGREENVAL(col)<<8)|(FXBLUEVAL(col));
296 }
297 for(int fill=width; fill<32; fill++){
298 *imgptr++=0;
299 }
300 }
301 if(height<32) memset(imgptr,0,(32-height)*32);
302
303 // Strawman mask bitmap
304 mask=CreateBitmap(32,32,1,1,NULL);
305 if(!mask){ throw FXImageException("unable to create cursor"); }
306
307 // Create cursor
308 ii.fIcon=FALSE;
309 ii.xHotspot=hotx;
310 ii.yHotspot=hoty;
311 ii.hbmMask=mask;
312 ii.hbmColor=img;
313 xid=CreateIconIndirect(&ii);
314
315 // No longer needed
316 DeleteObject(mask);
317 DeleteObject(img);
318 }
319
320 // No support for color cursor or simple black/white cursor
321 else{
322 FXint i,j,srcbytes,srcoffset,dstoffset; FXuchar tmpxor[128],tmpand[128];
323 FXTRACE((100,"%s::create: custom b/w %dx%d cursor\n",getClassName(),width,height));
324 srcbytes=(width+7)/8;
325 srcoffset=dstoffset=0;
326 memset(tmpand,0xff,sizeof(tmpand));
327 memset(tmpxor,0,sizeof(tmpxor));
328 for(j=0; j<height; j++){
329 for(i=0; i<width; i++){
330 if(((FXuchar*)(data+srcoffset+i))[3]>=128){
331 tmpand[dstoffset+(i>>3)]&=~(128>>(i&7));
332 if(!DARKCOLOR(((FXuchar*)(data+srcoffset+i))[0],((FXuchar*)(data+srcoffset+i))[1],((FXuchar*)(data+srcoffset+i))[2])){
333 tmpxor[dstoffset+(i>>3)]|=(128>>(i&7));
334 }
335 }
336 }
337 srcoffset+=width;
338 dstoffset+=4;
339 }
340 xid=CreateCursor((HINSTANCE)(getApp()->display),hotx,hoty,32,32,tmpand,tmpxor);
341 }
342 }
343
344 #endif
345
346 // Were we successful?
347 if(!xid){ throw FXImageException("unable to create cursor"); }
348
349 // Release pixel buffer
350 if(!(options&CURSOR_KEEP)) release();
351 }
352 }
353 }
354
355
356 // Detach cursor
detach()357 void FXCursor::detach(){
358 if(xid){
359 FXTRACE((100,"%s::detach %p\n",getClassName(),this));
360 xid=0;
361 }
362 }
363
364
365 // Release pixels buffer if it was owned
release()366 void FXCursor::release(){
367 if(options&CURSOR_OWNED){
368 options&=~CURSOR_OWNED;
369 FXFREE(&data);
370 }
371 data=NULL;
372 }
373
374
375 // Destroy cursor
destroy()376 void FXCursor::destroy(){
377 if(xid){
378 if(getApp()->isInitialized()){
379 FXTRACE((100,"%s::destroy %p\n",getClassName(),this));
380 #ifndef WIN32
381
382 // Delete cursor
383 XFreeCursor(DISPLAY(getApp()),xid);
384 #else
385
386 // Delete cursor
387 DestroyCursor((HCURSOR)xid);
388
389 #endif
390 }
391 xid=0;
392 }
393 }
394
395
396 // Save pixel data only
savePixels(FXStream & store) const397 FXbool FXCursor::savePixels(FXStream& store) const {
398 FXuint size=width*height;
399 store.save(data,size);
400 return TRUE;
401 }
402
403
404 // Load pixel data only
loadPixels(FXStream & store)405 FXbool FXCursor::loadPixels(FXStream& store){
406 FXuint size=width*height;
407 if(options&CURSOR_OWNED){FXFREE(&data);}
408 if(!FXMALLOC(&data,FXColor,size)) return FALSE;
409 store.load(data,size);
410 options|=CURSOR_OWNED;
411 return TRUE;
412 }
413
414
415 // Save cursor to stream
save(FXStream & store) const416 void FXCursor::save(FXStream& store) const {
417 FXuchar haspixels=(data!=NULL);
418 FXId::save(store);
419 store << width << height << hotx << hoty;
420 store << options;
421 store << haspixels;
422 if(haspixels) savePixels(store);
423 }
424
425
426 // Load cursor from stream
load(FXStream & store)427 void FXCursor::load(FXStream& store){
428 FXuchar haspixels;
429 FXId::load(store);
430 store >> width >> height >> hotx >> hoty;
431 store >> options;
432 store >> haspixels;
433 if(haspixels) loadPixels(store);
434 }
435
436
437 // Clean up
~FXCursor()438 FXCursor::~FXCursor(){
439 FXTRACE((100,"FXCursor::~FXCursor %p\n",this));
440 destroy();
441 if(options&CURSOR_OWNED){FXFREE(&data);}
442 data=(FXColor *)-1L;
443 }
444
445 }
446