File: xfixes-cursor-image.pl

package info (click to toggle)
libx11-protocol-other-perl 28-1%2Bdeb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 1,692 kB
  • ctags: 556
  • sloc: perl: 17,055; ansic: 624; sh: 238; lisp: 143; makefile: 39
file content (201 lines) | stat: -rwxr-xr-x 7,158 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/usr/bin/perl -w

# Copyright 2011, 2012, 2013 Kevin Ryde

# This file is part of X11-Protocol-Other.
#
# X11-Protocol-Other is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation; either version 3, or (at your option) any
# later version.
#
# X11-Protocol-Other is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with X11-Protocol-Other.  If not, see <http://www.gnu.org/licenses/>.


# Usage: perl xfixes-cursor-image.pl
#
# This is an example of getting the mouse pointer cursor image with XFIXES.
#
# $X->XFixesGetCursorImage() retrieves the cursor image.  CursorNotify
# events report when the image changes.  A change is normally due to moving
# into a window with a different "cursor" attribute, but may also be a
# pointer grab, or even an animated changing cursor from the RENDER
# extension.  See cursor-font-anim.pl for some fun with an animated root
# window cursor.
#
# The only painful thing is that XFixesGetCursorImage() gives 8-bit RGBA, so
# it's necessary to allocate colours etc to display that in a window.  In
# the code here the image is drawn to a pixmap, then that pixmap drawn to
# the window under Expose.
#
# $X->XFixesGetCursorImage() isn't done in the "event_handler" code because
# it's a round-trip request and waiting for the reply might read new events
# and call the event_handler recursively.  If badly lagged and continually
# receiving CursorNotify then that could be a very deep recursion, or make a
# mess of the drawing code.  So the event_handler just notes a fresh
# XFixesGetCursorImage() is required and that's done in the main loop after
# $X->handle_input().
#
# With only the core X protocol there's no good way to get the current
# cursor or its image.  The cursor attribute on a window can't be read back
# with GetWindowAttributes(), and all the area copying things such as
# GetImage() ignore the cursor.
#
# Things Not Done:
#
# The display window is a fixed 63x63 and the image positioned so the
# hotspot is always at 31,31.  This fits a cursor of up to 32x32.  A real
# program might centre the hotspot in the current window size (listening to
# ConfigureNotify), and might make the pixmap only the size of the cursor
# then draw it at the right place.
#
# The ChangeGC() plus PolyPoint() for each pixel is a bit wasteful.  Better
# would be to send all the pixels in one PutImage(), but building the
# server's required bit units, byte order and padding is a bit like hard
# work.
#
# The alpha channel in the cursor image is only used to draw or not draw
# each pixel.  It could be combined with the grey window background without
# too much trouble.  What's the right multiplication for alpha weighting?
# In core protocol the cursor pixels are always fully-opaque or
# fully-transparent, but XFIXES can make partial-transparent cursors.
#

use 5.004;
use strict;
use X11::Protocol;
use X11::AtomConstants;
use X11::Protocol::WM;

# uncomment this to run the ### lines
#use Smart::Comments;

my $X = X11::Protocol->new;
if (! $X->init_extension('XFIXES')) {
  print "XFIXES extension not available on the server\n";
  exit 1;
}

my $colormap = $X->default_colormap;

# rgb8_to_pixel() takes colour components 0 to 255 and returns a pixel value
# suitable for $window and $pixmap.  Black and white pixel values from the
# $X screen info are pre-loaded, other colours have to be allocated.
#
my %allocated_pixels = ('0.0.0'       => $X->black_pixel,
                        '255.255.255' => $X->white_pixel);
sub rgb8_to_pixel {
  my ($red, $green, $blue) = @_;
  my $key = "$red.$green.$blue";
  my $pixel = $allocated_pixels{$key};
  if (! defined $pixel) {
    ($pixel) = $X->AllocColor ($colormap,
                               $red * 0x101, $green * 0x101, $blue * 0x101);
    $allocated_pixels{$key} = $pixel;
  }
  return $pixel;
}

# grey colour
my ($background_pixel) =$X->AllocColor ($colormap, 0x9000,0x9000,0x9000);

my $window = $X->new_rsrc;
$X->CreateWindow ($window,
                  $X->root,         # parent
                  'InputOutput',    # class
                  $X->root_depth,   # depth
                  'CopyFromParent', # visual
                  0,0,              # x,y
                  63,63,            # w,h initial size
                  0,                # border
                  background_pixel => $background_pixel,
                  event_mask       => $X->pack_event_mask('Exposure'),
                 );
X11::Protocol::WM::set_wm_name ($X, $window, 'Current Cursor'); # title
X11::Protocol::WM::set_wm_icon_name ($X, $window, 'Cursor');
X11::Protocol::WM::set_wm_client_machine_from_syshostname ($X, $window);
X11::Protocol::WM::set_net_wm_pid ($X, $window);

my $pixmap = $X->new_rsrc;
$X->CreatePixmap ($pixmap,
                  $window,
                  $X->root_depth,
                  63,63);  # width,height

my $gc = $X->new_rsrc;
$X->CreateGC ($gc, $pixmap,
              # don't want NoExpose events when copying from $pixmap
              graphics_exposures => 0);


my $want_get_image = 1;
my $current_cursor_serial = -1;

$X->{'event_handler'} = sub {
  my (%h) = @_;
  ### event_handler: \%h

  if ($h{'name'} eq 'XFixesCursorNotify') {
    if ($h{'cursor_serial'} != $current_cursor_serial) {
      $want_get_image = 1;
    }

  } elsif ($h{'name'} eq 'Expose') {
    $X->CopyArea ($pixmap, $window, $gc,
                  0,0,    # src x,y
                  63,63,  # src w,h
                  0,0);   # dst x,y
  }
};

$X->XFixesSelectCursorInput ($window, 1);
$X->MapWindow($window);

for (;;) {
  $X->handle_input;

  if ($want_get_image) {
    my ($root_x,$root_y, $width,$height, $xhot,$yhot, $serial, $cursor_pixels)
      = $X->XFixesGetCursorImage;
    $current_cursor_serial = $serial;

    $X->ChangeGC ($gc, foreground => $background_pixel);
    $X->PolyFillRectangle ($pixmap, $gc, [0,0, 63,63]);

    my $pos = 0;
    foreach my $y (0 .. $height-1) {
      foreach my $x (0 .. $width-1) {

        my $argb = unpack 'L', substr($cursor_pixels,$pos,4);
        my $alpha = ($argb >> 24) & 0xFF;
        my $red   = ($argb >> 16) & 0xFF;
        my $green = ($argb >> 8)  & 0xFF;
        my $blue  =  $argb        & 0xFF;
        $pos += 4;

        if ($alpha >= 128) {  # opaque, ie. not transparent
          my $pixmap_pixel = rgb8_to_pixel($red, $green, $blue);
          $X->ChangeGC ($gc, foreground => $pixmap_pixel);
          $X->PolyPoint ($pixmap, $gc, 'Origin',
                         # hotspot at position x=31,y=31 in the display
                         $x + 31-$xhot,
                         $y + 31-$yhot);
        }
      }
    }
    $X->CopyArea ($pixmap, $window, $gc,
                  0,0,    # src x,y
                  63,63,  # src w,h
                  0,0);   # dst x,y

    # print "Cursor size ${width}x${height}\n";
  }
}

exit 0;