Last active
August 29, 2015 14:06
-
-
Save 3v1n0/092159ad27399ad17cd6 to your computer and use it in GitHub Desktop.
XYPos
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Gtk; | |
// Compile with | |
// valac --pkg gtk+-3.0 --pkg gdk-x11-3.0 -X $(pkg-config xi --libs) xypos.vala | |
[CCode (gir_namespace = "xi", cprefix = "", lower_case_cprefix = "", cheader_filename = "X11/extensions/XInput2.h")] | |
namespace XI | |
{ | |
[Compact] | |
[CCode (cname = "Display")] | |
public class Display : X.Display | |
{ | |
[CCode (cname = "XIQueryVersion")] | |
public extern X.Status xi_query_version(ref int maj, ref int min); | |
[CCode (cname = "XISelectEvents")] | |
public extern X.Status xi_select_events(X.Window win, EventMask[] masks); | |
[CCode (cname = "XSync")] | |
public extern int sync(bool discard = false); | |
} | |
[CCode (cname = "XIEventMask", has_type_id = false)] | |
public struct EventMask | |
{ | |
public int deviceid; | |
public int mask_len; | |
uchar* mask; | |
} | |
[CCode (cprefix = "XI_")] | |
[Flags] | |
public enum EventType | |
{ | |
DeviceChanged = 1, | |
KeyPress = 2, | |
KeyRelease = 3, | |
ButtonPress = 4, | |
ButtonRelease = 5, | |
Motion = 6, | |
Enter = 7, | |
Leave = 8, | |
FocusIn = 9, | |
FocusOut = 10, | |
HierarchyChanged = 11, | |
PropertyEvent = 12, | |
RawKeyPress = 13, | |
RawKeyRelease = 14, | |
RawButtonPress = 15, | |
RawButtonRelease = 16, | |
RawMotion = 17, | |
TouchBegin = 18, | |
TouchUpdate = 19, | |
TouchEnd = 20, | |
TouchOwnership = 21, | |
RawTouchBegin = 22, | |
RawTouchUpdate = 23, | |
RawTouchEnd = 24, | |
BarrierHit = 25, | |
BarrierLeave = 26, | |
LAST_EVENT = BarrierLeave, | |
} | |
[CCode (cname = "XIDeviceEvent")] | |
public struct DeviceEvent | |
{ | |
int type; | |
ulong serial; | |
bool send_event; | |
unowned Display display; | |
int extension; | |
int evtype; | |
X.Time time; | |
int deviceid; | |
int sourceid; | |
int detail; | |
X.Window root; | |
X.Window event; | |
X.Window child; | |
double root_x; | |
double root_y; | |
double event_x; | |
double event_y; | |
int flags; | |
ButtonState buttons; | |
ValuatorState valuators; | |
ModifierState mods; | |
GroupState group; | |
} | |
[CCode (cname = "XIButtonState")] | |
public struct ButtonState | |
{ | |
int mask_len; | |
uchar *mask; | |
} | |
[CCode (cname = "XIValuatorState")] | |
public struct ValuatorState | |
{ | |
int mask_len; | |
uchar *mask; | |
double values; | |
} | |
[CCode (cname = "XIModifierState")] | |
public struct ModifierState | |
{ | |
int base; | |
int latched; | |
int locked; | |
int effective; | |
} | |
[CCode (cname = "XIGroupState")] | |
public struct GroupState : ModifierState {} | |
// [CCode (cname = "XIMaskLen")] | |
// extern int mask_len(EventType mask); | |
int mask_len(EventType mask) { return ((mask) >> 3) + 1; } | |
[Flags] | |
public enum Devices | |
{ | |
[CCode (cname = "XIAllDevices")] | |
ALL = 0, | |
[CCode (cname = "XIAllMasterDevices")] | |
MASTER = 1 | |
} | |
// [CCode (cname = "XISetMask")] | |
// int set_mask(uchar* type, EventType event); | |
int set_mask(uchar *type, EventType event) { return type[(event)>>3] |= (1 << ((event) & 7)); } | |
} | |
class XYPos | |
{ | |
private const int TEXT_OFFSET = 10; | |
private const double CROSS_SIZE = 30; | |
private Gtk.Window win; | |
private int xi_opcode; | |
private double last_x; | |
private double last_y; | |
public XYPos() | |
{ | |
win = new Window(WindowType.POPUP); | |
win.set_type_hint(Gdk.WindowTypeHint.DOCK); | |
win.set_skip_pager_hint(true); | |
win.set_skip_taskbar_hint(true); | |
win.stick(); | |
win.set_app_paintable(true); | |
win.set_title("Test Mouse Position"); | |
win.set_decorated(false); | |
win.set_keep_above(true); | |
win.set_resizable(false); | |
win.set_focus_on_map(false); | |
win.set_accept_focus(false); | |
win.input_shape_combine_region(new Cairo.Region.rectangle({0, 0, 0, 0})); | |
win.get_screen().size_changed.connect(update_window); | |
win.get_screen().composited_changed.connect(update_window); | |
win.realize.connect(() => { | |
win.get_window().set_background_rgba({0, 0, 0, 0}); | |
}); | |
win.get_style_context().add_class("xypos"); | |
var style = new Gtk.CssProvider(); | |
style.load_from_data(" | |
.xypos { | |
color: #000; | |
text-shadow: 1px 0 #fff, -1px 0 #fff, 0 1px #fff, 0 -1px #fff; | |
}", -1); | |
win.get_style_context().add_provider(style, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); | |
win.draw.connect((widget, ctx) => { | |
var screen = win.get_screen(); | |
ctx.set_line_width(1.5); | |
ctx.set_dash({1.0}, 0); | |
ctx.set_source_rgb(1, 1, 1); | |
ctx.move_to(last_x, 0); | |
ctx.line_to(last_x, screen.get_height()); | |
ctx.move_to(0, last_y); | |
ctx.line_to(screen.get_width(), last_y); | |
ctx.stroke_preserve(); //Slow things down a lot :( | |
ctx.set_source_rgb(0, 0, 0); | |
ctx.set_dash({1.0}, 1); | |
ctx.stroke(); | |
ctx.set_line_width(4); | |
ctx.set_source_rgb(1, 1, 1); | |
ctx.set_dash({}, 0); | |
ctx.move_to(last_x - CROSS_SIZE/2, last_y); | |
ctx.line_to(last_x + CROSS_SIZE/2, last_y); | |
ctx.move_to(last_x, last_y - CROSS_SIZE/2); | |
ctx.line_to(last_x, last_y + CROSS_SIZE/2); | |
ctx.stroke_preserve(); | |
ctx.set_source_rgb(0, 0, 0); | |
ctx.set_line_width(2); | |
ctx.stroke(); | |
var pango = Gdk.pango_context_get(); | |
var layout = new Pango.Layout(pango); | |
layout.set_text(@"$((int)last_x) × $((int)last_y)", -1); | |
int text_w, text_h; | |
layout.get_pixel_size(out text_w, out text_h); | |
double text_x = last_x + TEXT_OFFSET; | |
double text_y = last_y + TEXT_OFFSET; | |
Gdk.Rectangle monitor_geo; | |
screen.get_monitor_geometry(screen.get_monitor_at_point((int)last_x, (int)last_y), out monitor_geo); | |
if (last_x > monitor_geo.x + monitor_geo.width / 2) | |
text_x = last_x - TEXT_OFFSET - text_w; | |
if (last_y > monitor_geo.y + monitor_geo.height / 2) | |
text_y = last_y - TEXT_OFFSET - text_h; | |
win.get_style_context().render_layout(ctx, text_x, text_y, layout); | |
return true; | |
}); | |
update_window(); | |
initialize_xi_monitor(); | |
} | |
private void update_window() | |
{ | |
var screen = win.get_screen(); | |
win.move(0,0); | |
win.set_size_request(screen.get_width(), screen.get_height()); | |
var visual = screen.get_rgba_visual(); | |
if (visual == null) | |
visual = screen.get_system_visual(); | |
win.set_visual(visual); | |
} | |
private Gdk.FilterReturn on_monitor_event(Gdk.XEvent xev, Gdk.Event ev) | |
{ | |
var e = (X.Event*) xev; | |
if (e.type != X.EventType.GenericEvent) | |
return Gdk.FilterReturn.CONTINUE; | |
XI.DeviceEvent *devent = e.xcookie.data; | |
last_x = devent.root_x; | |
last_y = devent.root_y; | |
win.queue_draw(); | |
return Gdk.FilterReturn.REMOVE; | |
} | |
private int last_xi; | |
private int last_yi; | |
private bool initialize_xi_monitor() | |
{ | |
// var dpy = new XI.Display(); | |
// win.map.connect(() => { | |
// X.Event ev = {0}; | |
// while (true) | |
// { | |
// dpy.next_event(ref ev); | |
// if (ev.type != X.EventType.GenericEvent) | |
// continue; | |
// XI.DeviceEvent *devent = ev.xcookie.data; | |
// message(@"Motion Event at $(devent.root_x)x$(devent.root_y)"); | |
// } | |
// }); | |
unowned XI.Display dpy = (XI.Display) Gdk.x11_get_default_xdisplay(); | |
int event_base, error_base; | |
if (!dpy.query_extension("XInputExtension", out xi_opcode, out event_base, out error_base)) | |
return false; | |
int maj = 2, min = 2; | |
if (dpy.xi_query_version(ref maj, ref min) != X.ErrorCode.SUCCESS) | |
return false; | |
uchar[] all_devs_bits = new uchar[XI.mask_len(XI.EventType.LAST_EVENT)]; | |
XI.EventMask all_devs = { XI.Devices.ALL, all_devs_bits.length, all_devs_bits }; | |
XI.set_mask(all_devs.mask, XI.EventType.Motion); | |
XI.set_mask(all_devs.mask, XI.EventType.KeyPress); | |
dpy.xi_select_events(dpy.default_root_window(), {all_devs}); | |
dpy.sync(); | |
Gdk.get_default_root_window().add_filter((xev, ev) => { | |
var e = (X.Event*) xev; | |
if (e.type != X.EventType.GenericEvent) | |
return Gdk.FilterReturn.CONTINUE; | |
XI.DeviceEvent *devent = e.xcookie.data; | |
if (devent.evtype == XI.EventType.Motion) | |
{ | |
last_x = devent.root_x; | |
last_y = devent.root_y; | |
win.queue_draw(); | |
debug(@"Motion Event at $(devent.root_x)x$(devent.root_y)"); | |
} | |
else if (devent.evtype == XI.EventType.KeyPress) | |
{ | |
if (devent.detail == 9) // Escape | |
Gtk.main_quit(); | |
} | |
return Gdk.FilterReturn.REMOVE; | |
}); | |
win.get_display().get_device_manager().get_client_pointer().get_position_double(null, out last_x, out last_y); | |
return true; | |
} | |
public void show() | |
{ | |
win.show_all(); | |
} | |
} | |
int main(string[] args) | |
{ | |
Gtk.init(ref args); | |
var xypos = new XYPos(); | |
xypos.show(); | |
Gtk.main(); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment