Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@3v1n0
Last active August 29, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 3v1n0/092159ad27399ad17cd6 to your computer and use it in GitHub Desktop.
Save 3v1n0/092159ad27399ad17cd6 to your computer and use it in GitHub Desktop.
XYPos
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