/*-
* Copyright (c) 1997-2002 The Protein Laboratory, University of Copenhagen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id$
*/
#include "unix/guts.h"
#include "Icon.h"
static int
cursor_map[] = {
/* crArrow => */ XC_left_ptr,
/* crText => */ XC_xterm,
/* crWait => */ XC_watch,
/* crSize => */ XC_sizing,
/* crMove => */ XC_fleur,
/* crSizeWest => */ XC_left_side,
/* crSizeEast => */ XC_right_side,
/* crSizeNE => */ XC_sb_h_double_arrow,
/* crSizeNorth => */ XC_top_side,
/* crSizeSouth => */ XC_bottom_side,
/* crSizeNS => */ XC_sb_v_double_arrow,
/* crSizeNW => */ XC_top_left_corner,
/* crSizeSE => */ XC_bottom_right_corner,
/* crSizeNE => */ XC_top_right_corner,
/* crSizeSW => */ XC_bottom_left_corner,
/* crInvalid => */ XC_X_cursor,
};
Cursor
predefined_cursors[] = {
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None
};
static int
get_cursor( Handle self, Pixmap *source, Pixmap *mask, Point *hot_spot, Cursor *cursor)
{
int id = X(self)-> pointer_id;
while ( self && ( id = X(self)-> pointer_id) == crDefault)
self = PWidget(self)-> owner;
if ( id == crDefault) {
id = crArrow;
} else if ( id == crUser) {
if (source) *source = X(self)-> user_p_source;
if (mask) *mask = X(self)-> user_p_mask;
if (hot_spot) *hot_spot = X(self)-> pointer_hot_spot;
if (cursor) *cursor = X(self)-> user_pointer;
}
return id;
}
static Bool
load_pointer_font( void)
{
if ( !guts.pointer_font)
guts.pointer_font = XLoadQueryFont( DISP, "cursor");
if ( !guts.pointer_font) {
warn( "Cannot load cursor font");
return false;
}
return true;
}
Point
apc_pointer_get_hot_spot( Handle self)
{
Point hot_spot;
int idx;
int id = get_cursor(self, nil, nil, &hot_spot, nil);
XFontStruct *fs;
XCharStruct *cs;
Point ret = {0,0};
if ( id < crDefault || id > crUser) return ret;
if ( id == crUser) return hot_spot;
if ( !load_pointer_font()) return ret;
idx = cursor_map[id];
fs = guts.pointer_font;
if ( !fs-> per_char)
cs = &fs-> min_bounds;
else if ( idx < fs-> min_char_or_byte2 || idx > fs-> max_char_or_byte2) {
int default_char = fs-> default_char;
if ( default_char < fs-> min_char_or_byte2 || default_char > fs-> max_char_or_byte2)
default_char = fs-> min_char_or_byte2;
cs = fs-> per_char + default_char - fs-> min_char_or_byte2;
} else
cs = fs-> per_char + idx - fs-> min_char_or_byte2;
ret. x = -cs->lbearing;
ret. y = guts.cursor_height - cs->ascent;
if ( ret. x < 0) ret. x = 0;
if ( ret. y < 0) ret. y = 0;
if ( ret. x >= guts. cursor_width) ret. x = guts. cursor_width - 1;
if ( ret. y >= guts. cursor_height) ret. y = guts. cursor_height - 1;
return ret;
}
Point
apc_pointer_get_pos( Handle self)
{
Point p;
XWindow root, child;
int x, y;
unsigned int mask;
if ( !XQueryPointer( DISP, guts. root,
&root, &child, &p. x, &p. y,
&x, &y, &mask))
return guts. displaySize;
p. y = guts. displaySize. y - p. y - 1;
return p;
}
int
apc_pointer_get_shape( Handle self)
{
return X(self)->pointer_id;
}
Point
apc_pointer_get_size( Handle self)
{
Point p;
p.x = guts.cursor_width;
p.y = guts.cursor_height;
return p;
}
Bool
apc_pointer_get_bitmap( Handle self, Handle icon)
{
XImage *im;
int id;
Pixmap p1 = None, p2 = None;
Bool free_pixmap = true;
GC gc;
XGCValues gcv;
char c;
int w = guts.cursor_width, h = guts.cursor_height;
id = get_cursor( self, &p1, &p2, nil, nil);
if ( id < crDefault || id > crUser) return false;
if ( id == crUser) {
if ( !p1 || !p2) {
warn( "User pointer inconsistency");
return false;
}
free_pixmap = false;
} else {
XFontStruct *fs;
XCharStruct *cs;
int idx = cursor_map[id];
if ( !load_pointer_font()) return false;
fs = guts.pointer_font;
if ( !fs-> per_char)
cs = &fs-> min_bounds;
else if ( idx < fs-> min_char_or_byte2 || idx > fs-> max_char_or_byte2) {
int default_char = fs-> default_char;
if ( default_char < fs-> min_char_or_byte2 || default_char > fs-> max_char_or_byte2)
default_char = fs-> min_char_or_byte2;
cs = fs-> per_char + default_char - fs-> min_char_or_byte2;
} else
cs = fs-> per_char + idx - fs-> min_char_or_byte2;
p1 = XCreatePixmap( DISP, guts. root, w, h, 1);
p2 = XCreatePixmap( DISP, guts. root, w, h, 1);
gcv. background = 1;
gcv. foreground = 0;
gcv. font = guts.pointer_font-> fid;
gc = XCreateGC( DISP, p1, GCBackground | GCForeground | GCFont, &gcv);
XFillRectangle( DISP, p1, gc, 0, 0, w, h);
gcv. background = 0;
gcv. foreground = 1;
XChangeGC( DISP, gc, GCBackground | GCForeground, &gcv);
XFillRectangle( DISP, p2, gc, 0, 0, w, h);
XDrawString( DISP, p1, gc, -cs-> lbearing, cs-> ascent, (c = (char)(idx+1), &c), 1);
gcv. background = 1;
gcv. foreground = 0;
XChangeGC( DISP, gc, GCBackground | GCForeground, &gcv);
XDrawString( DISP, p2, gc, -cs-> lbearing, cs-> ascent, (c = (char)(idx+1), &c), 1);
XDrawString( DISP, p1, gc, -cs-> lbearing, cs-> ascent, (c = (char)idx, &c), 1);
XFreeGC( DISP, gc);
}
CIcon(icon)-> create_empty( icon, w, h, imBW);
im = XGetImage( DISP, p1, 0, 0, w, h, 1, XYPixmap);
prima_copy_xybitmap( PIcon(icon)-> data, (Byte*)im-> data,
PIcon(icon)-> w, PIcon(icon)-> h,
PIcon(icon)-> lineSize, im-> bytes_per_line);
XDestroyImage( im);
im = XGetImage( DISP, p2, 0, 0, w, h, 1, XYPixmap);
prima_copy_xybitmap( PIcon(icon)-> mask, (Byte*)im-> data,
PIcon(icon)-> w, PIcon(icon)-> h,
PIcon(icon)-> maskLine, im-> bytes_per_line);
if ( id == crUser) {
int i;
Byte * mask = PIcon(icon)-> mask;
for ( i = 0; i < PIcon(icon)-> maskSize; i++)
mask[i] = ~mask[i];
}
XDestroyImage( im);
if ( free_pixmap) {
XFreePixmap( DISP, p1);
XFreePixmap( DISP, p2);
}
return true;
}
Bool
apc_pointer_get_visible( Handle self)
{
return guts. pointer_invisible_count == 0;
}
Bool
apc_pointer_set_pos( Handle self, int x, int y)
{
XEvent ev;
if ( !XWarpPointer( DISP, None, guts. root,
0, 0, guts. displaySize.x, guts. displaySize.y, x, guts. displaySize.y - y - 1))
return false;
XCHECKPOINT;
XSync( DISP, false);
while ( XCheckMaskEvent( DISP, PointerMotionMask|EnterWindowMask|LeaveWindowMask, &ev))
prima_handle_event( &ev, nil);
return true;
}
Bool
apc_pointer_set_shape( Handle self, int id)
{
DEFXX;
Cursor uc = None;
if ( id < crDefault || id > crUser) return false;
XX-> pointer_id = id;
id = get_cursor( self, nil, nil, nil, &uc);
if ( id == crUser) {
if ( uc != None || ( uc = XX-> user_pointer) != None) {
if ( self != application) {
if ( guts. pointer_invisible_count < 0) {
if ( !XX-> flags. pointer_obscured) {
XDefineCursor( DISP, XX-> udrawable, prima_null_pointer());
XX-> flags. pointer_obscured = 1;
}
} else {
XDefineCursor( DISP, XX-> udrawable, uc);
XX-> flags. pointer_obscured = 0;
}
XCHECKPOINT;
}
} else
id = crArrow;
}
if ( id != crUser) {
if ( predefined_cursors[id] == None) {
predefined_cursors[id] =
XCreateFontCursor( DISP, cursor_map[id]);
XCHECKPOINT;
}
XX-> actual_pointer = predefined_cursors[id];
if ( self != application) {
if ( guts. pointer_invisible_count < 0) {
if ( !XX-> flags. pointer_obscured) {
XDefineCursor( DISP, XX-> udrawable, prima_null_pointer());
XX-> flags. pointer_obscured = 1;
}
} else {
XDefineCursor( DISP, XX-> udrawable, predefined_cursors[id]);
XX-> flags. pointer_obscured = 0;
}
XCHECKPOINT;
}
}
XFlush( DISP);
if ( guts. grab_widget)
apc_widget_set_capture( guts. grab_widget, true, guts. grab_confine);
return true;
}
Bool
apc_pointer_set_user( Handle self, Handle icon, Point hot_spot)
{
DEFXX;
Handle cursor;
PIcon c;
if ( XX-> user_pointer != None) {
XFreeCursor( DISP, XX-> user_pointer);
XX-> user_pointer = None;
}
if ( XX-> user_p_source != None) {
XFreePixmap( DISP, XX-> user_p_source);
XX-> user_p_source = None;
}
if ( XX-> user_p_mask != None) {
XFreePixmap( DISP, XX-> user_p_mask);
XX-> user_p_mask = None;
}
if ( icon != nilHandle) {
Bool noSZ = PIcon(icon)-> w != guts.cursor_width || PIcon(icon)-> h != guts.cursor_height;
Bool noBPP = (PIcon(icon)-> type & imBPP) != 1;
XColor xcb, xcw;
if ( noSZ || noBPP) {
cursor = CIcon(icon)->dup(icon);
c = PIcon(cursor);
if ( cursor == nilHandle) {
warn( "Error duping user cursor");
return false;
}
if ( noSZ) {
CIcon(cursor)-> stretch( cursor, guts.cursor_width, guts.cursor_height);
if ( c-> w != guts.cursor_width || c-> h != guts.cursor_height) {
warn( "Error stretching user cursor");
Object_destroy( cursor);
return false;
}
}
if ( noBPP) {
CIcon(cursor)-> set_type( cursor, imMono);
if ((c-> type & imBPP) != 1) {
warn( "Error black-n-whiting user cursor");
Object_destroy( cursor);
return false;
}
}
} else
cursor = icon;
if ( !prima_create_icon_pixmaps( cursor, &XX-> user_p_source, &XX-> user_p_mask)) {
warn( "Error creating user cursor pixmaps");
if ( noSZ || noBPP)
Object_destroy( cursor);
return false;
}
if ( noSZ || noBPP)
Object_destroy( cursor);
if ( hot_spot. x < 0) hot_spot. x = 0;
if ( hot_spot. y < 0) hot_spot. y = 0;
if ( hot_spot. x >= guts. cursor_width) hot_spot. x = guts. cursor_width - 1;
if ( hot_spot. y >= guts. cursor_height) hot_spot. y = guts. cursor_height - 1;
XX-> pointer_hot_spot = hot_spot;
xcb. red = xcb. green = xcb. blue = 0;
xcw. red = xcw. green = xcw. blue = 0xFFFF;
xcb. pixel = guts. monochromeMap[0];
xcw. pixel = guts. monochromeMap[1];
xcb. flags = xcw. flags = DoRed | DoGreen | DoBlue;
XX-> user_pointer = XCreatePixmapCursor( DISP, XX-> user_p_source,
XX-> user_p_mask, &xcw, &xcb,
hot_spot. x, guts.cursor_height - hot_spot. y);
if ( XX-> user_pointer == None) {
warn( "error creating cursor from pixmaps");
return false;
}
if ( XX-> pointer_id == crUser && self != application) {
if ( guts. pointer_invisible_count < 0) {
if ( !XX-> flags. pointer_obscured) {
XDefineCursor( DISP, XX-> udrawable, prima_null_pointer());
XX-> flags. pointer_obscured = 1;
}
} else {
XDefineCursor( DISP, XX-> udrawable, XX-> user_pointer);
XX-> flags. pointer_obscured = 0;
}
XCHECKPOINT;
}
}
XFlush( DISP);
if ( guts. grab_widget)
apc_widget_set_capture( guts. grab_widget, true, guts. grab_confine);
return true;
}
Bool
apc_pointer_set_visible( Handle self, Bool visible)
{
/* maintaining hide/show count */
if ( visible) {
if ( guts. pointer_invisible_count == 0)
return true;
if ( ++guts. pointer_invisible_count < 0)
return true;
} else {
if ( guts. pointer_invisible_count-- < 0)
return true;
}
/* setting pointer for widget under cursor */
{
Point p = apc_pointer_get_pos( application);
Handle wij = apc_application_get_widget_from_point( application, p);
if ( wij) {
X(wij)-> flags. pointer_obscured = (visible ? 0 : 1);
XDefineCursor( DISP, X(wij)-> udrawable,
visible ? (( X(wij)-> pointer_id == crUser) ?
X(wij)-> user_pointer : X(wij)-> actual_pointer)
: prima_null_pointer());
}
}
XFlush( DISP);
if ( guts. grab_widget)
apc_widget_set_capture( guts. grab_widget, true, guts. grab_confine);
return true;
}
Cursor
prima_null_pointer( void)
{
if ( guts. null_pointer == nilHandle) {
Handle nullc = ( Handle) create_object( "Prima::Icon", "", nil);
PIcon n = ( PIcon) nullc;
Pixmap xor, and;
XColor xc;
if ( nullc == nilHandle) {
warn("Error creating icon object");
return nilHandle;
}
n-> self-> create_empty( nullc, 16, 16, imBW);
memset( n-> mask, 0xFF, n-> maskSize);
if ( !prima_create_icon_pixmaps( nullc, &xor, &and)) {
warn( "Error creating null cursor pixmaps");
Object_destroy( nullc);
return nilHandle;
}
Object_destroy( nullc);
xc. red = xc. green = xc. blue = 0;
xc. pixel = guts. monochromeMap[0];
xc. flags = DoRed | DoGreen | DoBlue;
guts. null_pointer = XCreatePixmapCursor( DISP, xor, and, &xc, &xc, 0, 0);
XCHECKPOINT;
XFreePixmap( DISP, xor);
XFreePixmap( DISP, and);
if ( !guts. null_pointer) {
warn( "Error creating null cursor from pixmaps");
return nilHandle;
}
}
return guts. null_pointer;
}