Class for interaction with clients
Methods
Public Class
Public Instance
- <=>
- ==
- []
- []=
- alive?
- eql?
- flags
- flags=
- focus
- geometry
- geometry=
- gravity
- gravity=
- has_focus?
- has_tag?
- hash
- instance
- is_borderless?
- is_fixed?
- is_float?
- is_full?
- is_resize?
- is_stick?
- is_urgent?
- is_zaphod?
- kill
- klass
- lower
- name
- pid
- raise
- retag
- role
- screen
- send_button
- send_key
- tag
- tags
- tags=
- to_str
- toggle_borderless
- toggle_fixed
- toggle_float
- toggle_full
- toggle_resize
- toggle_stick
- toggle_urgent
- toggle_zaphod
- untag
- update
- views
- win
Public Instance Aliases
+ | -> | tag |
Aliases |
- | -> | untag | |
click | -> | send_button | |
save | -> | update | |
to_s | -> | to_str |
Attributes
Public Class methods
Get current active Client.
Subtlext::Client.current => #<Subtlext::Client:xxx>
VALUE subextClientSingCurrent(VALUE self) { VALUE client = Qnil; unsigned long *focus = NULL; subextSubtlextConnect(NULL); ///< Implicit open connection /* Get current client */ if((focus = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_WINDOW, XInternAtom(display, "_NET_ACTIVE_WINDOW", False), NULL))) { /* Update client values */ if(RTEST(client = subextClientInstantiate(*focus))) subextClientUpdate(client); free(focus); } else rb_raise(rb_eStandardError, "Invalid current window"); return client; }
Find Client by a given value which can be of following type:
Fixnum |
Array index of the | ||||||||||||
String |
Regexp match against both | ||||||||||||
Hash |
Instead of just match
With one of following keys: :title, :name, :class, :gravity | ||||||||||||
Symbol |
Either :current for current View, :all for an array of all Views or any string for an exact match. |
Subtlext::Client.find(1) => [#<Subtlext::Client:xxx>] Subtlext::Client.find("subtle") => [#<Subtlext::Client:xxx>] Subtlext::Client[".*"] => [#<Subtlext::Client:xxx>, #<Subtlext::Client:xxx>] Subtlext::Client["subtle"] => [] Subtlext::Client[:terms] => [#<Subtlext::Client:xxx>] Subtlext::Client[name: "subtle"] => [#<Subtlext::Client:xxx>]
VALUE subextClientSingFind(VALUE self, VALUE value) { return ClientFind(value, False); }
Find first Client by a given value which can be of following type:
Fixnum |
Array index of the | ||||||||||||
String |
Regexp match against both | ||||||||||||
Hash |
Instead of just match
With one of following keys: :title, :name, :class, :gravity | ||||||||||||
Symbol |
Either :current for current View, :all for an array of all Views or any string for an exact match. |
Subtlext::Client.first(1) => #<Subtlext::Client:xxx> Subtlext::Client.first("subtle") => #<Subtlext::Client:xxx> Subtlext::Client.first(name: "subtle") => #<Subtlext::Client:xxx>
VALUE subextClientSingFirst(VALUE self, VALUE value) { return ClientFind(value, True); }
Get an array of all Clients based on _NET_CLIENT_LIST
property
list.
Subtlext::Client.list => [#<Subtlext::Client:xxx>, #<Subtlext::Client:xxx>] Subtlext::Client.list => []
VALUE subextClientSingList(VALUE self) { int i, nclients = 0; Window *clients = NULL; VALUE meth = Qnil, klass = Qnil, array = Qnil, client = Qnil; subextSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ meth = rb_intern("new"); array = rb_ary_new(); klass = rb_const_get(mod, rb_intern("Client")); clients = subextSubtlextWindowList("_NET_CLIENT_LIST", &nclients); /* Check results */ if(clients) { for(i = 0; i < nclients; i++) { /* Create client */ if(RTEST(client = rb_funcall(klass, meth, 1, LONG2NUM(clients[i])))) { subextClientUpdate(client); rb_ary_push(array, client); } } free(clients); } return array; }
Create a new Client object locally without calling save automatically.
The Client won't be visible until save is called.
client = Subtlext::Client.new(1) => #<Subtlext::Client:xxx>
VALUE subextClientInit(VALUE self, VALUE win) { if(!FIXNUM_P(win)) rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(win)); /* Init object */ rb_iv_set(self, "@win", win); rb_iv_set(self, "@name", Qnil); rb_iv_set(self, "@instance", Qnil); rb_iv_set(self, "@klass", Qnil); rb_iv_set(self, "@role", Qnil); rb_iv_set(self, "@geometry", Qnil); rb_iv_set(self, "@gravity", Qnil); rb_iv_set(self, "@screen", Qnil); rb_iv_set(self, "@flags", Qnil); rb_iv_set(self, "@tags", INT2FIX(0)); subextSubtlextConnect(NULL); ///< Implicit open connection return self; }
Get array of the last five active Clients.
Subtlext::Client.recent => [ #<Subtlext::Client:xxx> ]
VALUE subextClientSingRecent(VALUE self) { int i, nclients = 0; Window *clients = NULL; VALUE meth = Qnil, klass = Qnil, array = Qnil, client = Qnil; subextSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ meth = rb_intern("new"); array = rb_ary_new(); klass = rb_const_get(mod, rb_intern("Client")); clients = subextSubtlextWindowList("_NET_ACTIVE_WINDOW", &nclients); /* Check results */ if(clients) { for(i = 0; i < nclients; i++) { /* Create client */ if(!NIL_P(client = rb_funcall(klass, meth, 1, LONG2NUM(clients[i])))) { subextClientUpdate(client); rb_ary_push(array, client); } } free(clients); } return array; }
Click on a wondow and get the selected Client.
Subtlext::Client.select => #<Subtlext::Client:xxx>
VALUE subextClientSingSelect(VALUE self) { VALUE win = subextSubtleSingSelect(self); return None != NUM2LONG(win) ? subextClientSingFind(self, win) : Qnil; }
Spawn a command and returns a Client object.
spawn("xterm") => #<Subtlext::Client:xxx>
VALUE subextClientSingSpawn(VALUE self, VALUE cmd) { VALUE ret = Qnil; /* Check object type */ if(T_STRING == rb_type(cmd)) { pid_t pid = 0; subextSubtlextConnect(NULL); ///< Implicit open connection /* Create client with empty window id since we cannot * know the real window id at this point (race) */ if(0 < (pid = subSharedSpawn(RSTRING_PTR(cmd)))) { ret = subextClientInstantiate((Window)pid); rb_iv_set(ret, "@pid", INT2FIX((int)pid)); } } else rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(cmd)); return ret; }
Get array of all visible Client on all Views.
Subtlext::Client.visible => [#<Subtlext::Client:xxx>, #<Subtlext::Client:xxx>] Subtlext::Client.visible => []
VALUE subextClientSingVisible(VALUE self) { int i, nclients = 0; Window *clients = NULL; unsigned long *visible = NULL; VALUE meth = Qnil, klass = Qnil, array = Qnil, client = Qnil; subextSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ meth = rb_intern("new"); array = rb_ary_new(); klass = rb_const_get(mod, rb_intern("Client")); clients = subextSubtlextWindowList("_NET_CLIENT_LIST", &nclients); visible = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "SUBTLE_VISIBLE_TAGS", False), NULL); /* Check results */ if(clients && visible) { for(i = 0; i < nclients; i++) { unsigned long *tags = (unsigned long *)subSharedPropertyGet(display, clients[i], XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_TAGS", False), NULL); /* Create client on match */ if(tags && *tags && *visible & *tags && RTEST(client = rb_funcall(klass, meth, 1, LONG2NUM(clients[i])))) { subextClientUpdate(client); rb_ary_push(array, client); } if(tags) free(tags); } } if(clients) free(clients); if(visible) free(visible); return array; }
Public Instance methods
Whether both objects have the same value. Returns -1, 0 or 1 when self is less than, equal to or grater than other. (based on win)
object1 <=> object2 => 0
static VALUE SubtlextEqualSpaceWindow(VALUE self, VALUE other) { return SubtlextSpaceship(self, other, "@win"); }
Whether both objects have the same values (based on win)
object1 == object2 => true
static VALUE SubtlextEqualWindow(VALUE self, VALUE other) { return SubtlextEqual(self, other, "@win", False); }
Get arbitrary persistent property string or symbol value
object["wm"] => "subtle" object[:wm] => "subtle"
static VALUE SubtlextPropReader(VALUE self, VALUE key) { char *prop = NULL; VALUE ret = Qnil; /* Check ruby object */ rb_check_frozen(self); /* Check object type */ switch(rb_type(key)) { case T_STRING: prop = RSTRING_PTR(key); break; case T_SYMBOL: prop = (char *)SYM2CHAR(key); break; default: rb_raise(rb_eArgError, "Unexpected key value type `%s'", rb_obj_classname(key)); return Qnil; } /* Check results */ if(prop) { char propname[255] = { 0 }, *name = NULL, *result = NULL; Window win = ROOT; VALUE val = Qnil; /* Sanitize property name */ name = strdup(prop); SubtlextStringify(name); /* Check object type */ if(rb_obj_is_instance_of(self, rb_const_get(mod, rb_intern("View")))) { GET_ATTR(self, "@name", val); snprintf(propname, sizeof(propname), "SUBTLE_PROPERTY_%s_%s", RSTRING_PTR(val), name); } else ///< Client { GET_ATTR(self, "@win", val); win = NUM2LONG(val); snprintf(propname, sizeof(propname), "SUBTLE_PROPERTY_%s", name); } /* Get actual property */ if((result = subSharedPropertyGet(display, win, XInternAtom(display, "UTF8_STRING", False), XInternAtom(display, propname, False), NULL))) { ret = rb_str_new2(result); free(result); } free(name); } return ret; }
Set arbitrary persistent property string or symbol value
Symbols are implictly converted to string, to remove a property just set it
to nil
.
object["wm"] = "subtle" => nil object[:wm] = "subtle" => nil object[:wm] = nil => nil
static VALUE SubtlextPropWriter(VALUE self, VALUE key, VALUE value) { VALUE val = Qnil, str = value; char *prop = NULL, *name = NULL, propname[255] = { 0 }; Window win = ROOT; /* Check ruby object */ rb_check_frozen(self); /* Check object type */ switch(rb_type(key)) { case T_STRING: prop = RSTRING_PTR(key); break; case T_SYMBOL: prop = (char *)SYM2CHAR(key); break; default: rb_raise(rb_eArgError, "Unexpected key value-type `%s'", rb_obj_classname(key)); return Qnil; } /* Sanitize property name */ name = strdup(prop); SubtlextStringify(name); /* Assemble property name */ if(rb_obj_is_instance_of(self, rb_const_get(mod, rb_intern("View")))) { GET_ATTR(self, "@name", val); snprintf(propname, sizeof(propname), "SUBTLE_PROPERTY_%s_%s", RSTRING_PTR(val), name); } else ///< Client { GET_ATTR(self, "@win", val); win = NUM2LONG(val); snprintf(propname, sizeof(propname), "SUBTLE_PROPERTY_%s", name); } /* Check value type */ switch(rb_type(value)) { case T_SYMBOL: str = rb_sym_to_s(value); case T_STRING: XChangeProperty(display, win, XInternAtom(display, propname, False), XInternAtom(display, "UTF8_STRING", False), 8, PropModeReplace, (unsigned char *)RSTRING_PTR(str), RSTRING_LEN(str)); break; case T_NIL: XDeleteProperty(display, win, XInternAtom(display, propname, False)); break; default: rb_raise(rb_eArgError, "Unexpected value value-type `%s'", rb_obj_classname(value)); } XSync(display, False); ///< Sync all changes if(name) free(name); return Qnil; }
Check if client is alive.
client.alive? => true client.alive? => false
VALUE subextClientAskAlive(VALUE self) { VALUE ret = Qfalse, win = Qnil; XWindowAttributes attrs; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); subextSubtlextConnect(NULL); ///< Implicit open connection /* Fetch client attributes */ if(!XGetWindowAttributes(display, NUM2LONG(win), &attrs)) rb_obj_freeze(self); else ret = Qtrue; return ret; }
Whether both objects have the same value and types (based on win)
object1.eql? object2 => true
static VALUE SubtlextEqualTypedWindow(VALUE self, VALUE other) { return SubtlextEqual(self, other, "@win", True); }
Set multiple flags at once. Flags can be one or a combination of the following:
:full |
Set fullscreen mode |
:float |
Set floating mode |
:stick |
Set sticky mode |
:resize |
Set resize mode |
:urgent |
Set urgent mode |
:zaphod |
Set zaphod mode |
:fixed |
Set fixed mode |
:borderless |
Set borderless mode |
client.flags = [ :float, :stick ] => nil
VALUE subextClientFlagsWriter(VALUE self, VALUE value) { /* Check object type */ if(T_ARRAY == rb_type(value)) { int i, flags = 0; VALUE entry = Qnil; /* Translate flags */ for(i = 0; Qnil != (entry = rb_ary_entry(value, i)); ++i) { if(CHAR2SYM("full") == entry) flags |= SUB_EWMH_FULL; else if(CHAR2SYM("float") == entry) flags |= SUB_EWMH_FLOAT; else if(CHAR2SYM("stick") == entry) flags |= SUB_EWMH_STICK; else if(CHAR2SYM("resize") == entry) flags |= SUB_EWMH_RESIZE; else if(CHAR2SYM("urgent") == entry) flags |= SUB_EWMH_URGENT; else if(CHAR2SYM("zaphod") == entry) flags |= SUB_EWMH_ZAPHOD; else if(CHAR2SYM("fixed") == entry) flags |= SUB_EWMH_FIXED; else if(CHAR2SYM("borderless") == entry) flags |= SUB_EWMH_BORDERLESS; } ClientFlagsSet(self, flags, False); } return self; }
Set focus to window
object.focus => nil
static VALUE SubtlextFocus(VALUE self) { VALUE win = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); /* Send message */ data.l[0] = NUM2LONG(win); subSharedMessage(display, ROOT, "_NET_ACTIVE_WINDOW", data, 32, True); return self; }
VALUE subextClientGeometryReader(VALUE self) { VALUE win = Qnil, geom = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); subextSubtlextConnect(NULL); ///< Implicit open connection /* Load on demand */ if(NIL_P((geom = rb_iv_get(self, "@geometry")))) { XRectangle geometry = { 0 }; subSharedPropertyGeometry(display, NUM2LONG(win), &geometry); geom = subextGeometryInstantiate(geometry.x, geometry.y, geometry.width, geometry.height); rb_iv_set(self, "@geometry", geom); } return geom; }
Set Client geometry.
client.geometry = 0, 0, 100, 100 => 0 client.geometry = [ 0, 0, 100, 100 ] => [ 0, 0, 100, 100 ] client.geometry = { x: 0, y: 0, width: 100, height: 100 } => { x: 0, y: 0, width: 100, height: 100 } client.geometry = "0x0+100+100" => "0x0+100+100" client.geometry = Subtlext::Geometry(0, 0, 100, 100) => #<Subtlext::Geometry:xxx>
VALUE subextClientGeometryWriter(int argc, VALUE *argv, VALUE self) { VALUE klass = Qnil, geom = Qnil; /* Check ruby object */ rb_check_frozen(self); subextSubtlextConnect(NULL); ///< Implicit open connection /* Delegate arguments */ klass = rb_const_get(mod, rb_intern("Geometry")); geom = rb_funcall2(klass, rb_intern("new"), argc, argv); /* Update geometry */ if(RTEST(geom)) { VALUE win = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; GET_ATTR(self, "@win", win); data.l[1] = FIX2INT(rb_iv_get(geom, "@x")); data.l[2] = FIX2INT(rb_iv_get(geom, "@y")); data.l[3] = FIX2INT(rb_iv_get(geom, "@width")); data.l[4] = FIX2INT(rb_iv_get(geom, "@height")); subSharedMessage(display, NUM2LONG(win), "_NET_MOVERESIZE_WINDOW", data, 32, True); rb_iv_set(self, "@geometry", geom); } return geom; }
VALUE subextClientGravityReader(VALUE self) { VALUE win = Qnil, gravity = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); subextSubtlextConnect(NULL); ///< Implicit open connection /* Load on demand */ if(NIL_P((gravity = rb_iv_get(self, "@gravity")))) { int *id = NULL; char buf[5] = { 0 }; /* Get gravity */ if((id = (int *)subSharedPropertyGet(display, NUM2LONG(win), XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_GRAVITY", False), NULL))) { /* Create gravity */ snprintf(buf, sizeof(buf), "%d", *id); gravity = subextGravityInstantiate(buf); subextGravitySave(gravity); rb_iv_set(self, "@gravity", gravity); free(id); } } return gravity; }
Set Client Gravity either for current or for specific View.
# Set gravity for current view client.gravity = 0 => #<Subtlext::Gravity:xxx> client.gravity = :center => #<Subtlext::Gravity:xxx> client.gravity = Subtlext::Gravity[0] => #<Subtlext::Gravity:xxx> # Set gravity for specific view client.gravity = { :terms => :center } => #<Subtlext::Gravity:xxx>
VALUE subextClientGravityWriter(VALUE self, VALUE value) { /* Check ruby object */ rb_check_frozen(self); subextSubtlextConnect(NULL); ///< Implicit open connection /* Check value type */ switch(rb_type(value)) { case T_FIXNUM: case T_SYMBOL: case T_STRING: ClientGravity(Qnil, value, self); break; case T_OBJECT: if(rb_obj_is_instance_of(value, rb_const_get(mod, rb_intern("Gravity")))) ClientGravity(Qnil, value, self); break; case T_HASH: rb_hash_foreach(value, ClientGravity, self); break; default: rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } /* Reset gravity */ rb_iv_set(self, "@gravity", Qnil); return value; }
Check if window has focus
object.focus? => true object.focus? => false
static VALUE SubtlextAskFocus(VALUE self) { VALUE ret = Qfalse, win = Qnil; unsigned long *focus = NULL; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); /* Fetch data */ if((focus = (unsigned long *)subSharedPropertyGet(display, ROOT, XA_WINDOW, XInternAtom(display, "_NET_ACTIVE_WINDOW", False), NULL))) { if(*focus == NUM2LONG(win)) ret = Qtrue; free(focus); } return ret; }
SubtlextTagAsk {{{
static VALUE SubtlextTagAsk(VALUE self, VALUE value) { VALUE sym = Qnil, tag = Qnil, ret = Qfalse; /* Check ruby object */ rb_check_frozen(self); /* Check value type */ switch(rb_type(value)) { case T_STRING: sym = CHAR2SYM(RSTRING_PTR(value)); break; case T_SYMBOL: case T_OBJECT: sym = value; break; default: rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(value)); } /* Find tag */ if(RTEST(tag = subextTagSingFirst(Qnil, sym))) { VALUE id = Qnil, tags = Qnil; /* Get properties */ id = rb_iv_get(tag, "@id"); tags = rb_iv_get(self, "@tags"); if(FIX2INT(tags) & (1L << (FIX2INT(id) + 1))) ret = Qtrue; } return ret; }
Convert this object to hash.
puts object.hash => 1746246187916025425
static VALUE SubtlextHash(VALUE self) { VALUE str = Qnil, id = rb_intern("to_str"); /* Convert to string */ if(rb_respond_to(self, id)) str = rb_funcall(self, id, 0, Qnil); return T_STRING == rb_type(str) ? INT2FIX(rb_str_hash(str)) : Qnil; }
Check if Client is borderless.
client.is_borderless? => true client.is_borderless? => false
VALUE subextClientFlagsAskBorderless(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_BORDERLESS); }
Check if Client is fixed.
client.is_fixed? => true client.is_fixed? => false
VALUE subextClientFlagsAskFixed(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_FIXED); }
Check if Client is floating.
client.is_float? => true client.is_float? => false
VALUE subextClientFlagsAskFloat(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_FLOAT); }
Check if Client is fullscreen.
client.is_full? => true client.is_full? => false
VALUE subextClientFlagsAskFull(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_FULL); }
Check if Client uses size hints.
client.is_resize? => true client.is_resize? => false
VALUE subextClientFlagsAskResize(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_RESIZE); }
Check if Client is sticky.
client.is_stick? => true client.is_stick? => false
VALUE subextClientFlagsAskStick(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_STICK); }
Check if Client is urgent.
client.is_urgent? => true client.is_urgent? => false
VALUE subextClientFlagsAskUrgent(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_URGENT); }
Check if Client is zaphod.
client.is_zaphod? => true client.is_zaphod? => false
VALUE subextClientFlagsAskZaphod(VALUE self) { return ClientFlagsGet(self, SUB_EWMH_ZAPHOD); }
Send a close signal to Client and freeze this object.
client.kill => nil
VALUE subextClientKill(VALUE self) { VALUE win = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); subextSubtlextConnect(NULL); ///< Implicit open connection /* Send message */ data.l[0] = CurrentTime; data.l[1] = 2; ///< Claim to be a pager subSharedMessage(display, NUM2LONG(win), "_NET_CLOSE_WINDOW", data, 32, True); rb_obj_freeze(self); return Qnil; }
Move Client window to bottom of window stack.
client.raise => nil
VALUE subextClientRestackLower(VALUE self) { return ClientRestack(self, Below); }
Get window pid
object.pid => 123
static VALUE SubtlextPidReader(VALUE self) { Window win = None; VALUE pid = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); /* Load on demand */ if(NIL_P((pid = rb_iv_get(self, "@pid")))) { int *id = NULL; /* Get pid */ if((id = (int *)subSharedPropertyGet(display, win, XA_CARDINAL, XInternAtom(display, "_NET_WM_PID", False), NULL))) { pid = INT2FIX(*id); rb_iv_set(self, "@pid", pid); free(id); } } return pid; }
Move Client window to top of window stack.
client.raise => nil
VALUE subextClientRestackRaise(VALUE self) { return ClientRestack(self, Above); }
SubtlextTagReload {{{
static VALUE SubtlextTagReload(VALUE self) { VALUE win = Qnil; SubMessageData data = { { 0, 0, 0, 0, 0 } }; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); /* Send message */ data.l[0] = NUM2LONG(win); subSharedMessage(display, ROOT, "SUBTLE_CLIENT_RETAG", data, 32, True); return Qnil; }
VALUE subextClientScreenReader(VALUE self) { VALUE screen = Qnil, win = Qnil; int *id = NULL; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); /* Get screen */ if((id = (int *)subSharedPropertyGet(display, NUM2LONG(win), XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_SCREEN", False), NULL))) { screen = subextScreenSingFind(self, INT2FIX(*id)); free(id); } return screen; }
Emulate a click on a window with optional button and x/y position
object.send_button => nil object.send_button(2) => Object
static VALUE SubtlextSendButton(int argc, VALUE *argv, VALUE self) { Window subwin = None; XEvent event = { 0 }; VALUE button = Qnil, x = Qnil, y = Qnil, win = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); rb_scan_args(argc, argv, "03", &button, &x, &y); /* Assemble button event */ event.type = EnterNotify; event.xcrossing.window = NUM2LONG(win); event.xcrossing.root = ROOT; event.xcrossing.subwindow = NUM2LONG(win); event.xcrossing.same_screen = True; event.xcrossing.x = FIXNUM_P(x) ? FIX2INT(x) : 5; event.xcrossing.y = FIXNUM_P(y) ? FIX2INT(y) : 5; /* Translate window x/y to root x/y */ XTranslateCoordinates(display, event.xcrossing.window, event.xcrossing.root, event.xcrossing.x, event.xcrossing.y, &event.xcrossing.x_root, &event.xcrossing.y_root, &subwin); //XSetInputFocus(display, event.xany.window, RevertToPointerRoot, CurrentTime); XSendEvent(display, NUM2LONG(win), True, EnterWindowMask, &event); /* Send button press event */ event.type = ButtonPress; event.xbutton.button = FIXNUM_P(button) ? FIX2INT(button) : 1; XSendEvent(display, NUM2LONG(win), True, ButtonPressMask, &event); XFlush(display); usleep(12000); /* Send button release event */ event.type = ButtonRelease; XSendEvent(display, NUM2LONG(win), True, ButtonReleaseMask, &event); XFlush(display); return self; }
Emulate a keypress on a window
object.send_key("d") => Object
static VALUE SubtlextSendKey(int argc, VALUE *argv, VALUE self) { VALUE keys = Qnil, x = Qnil, y = Qnil, win = Qnil; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); rb_scan_args(argc, argv, "12", &keys, &x, &y); /* Check object type */ if(T_STRING == rb_type(keys)) { int mouse = False; unsigned int code = 0, state = 0; char *tokens = NULL, *tok = NULL, *save = NULL; Window subwin = None; KeySym sym = None; XEvent event = { 0 }; /* Assemble enter event */ event.type = EnterNotify; event.xcrossing.window = NUM2LONG(win); event.xcrossing.root = ROOT; event.xcrossing.subwindow = NUM2LONG(win); event.xcrossing.same_screen = True; event.xcrossing.x = FIXNUM_P(x) ? FIX2INT(x) : 5; event.xcrossing.y = FIXNUM_P(y) ? FIX2INT(y) : 5; /* Translate window x/y to root x/y */ XTranslateCoordinates(display, event.xcrossing.window, event.xcrossing.root, event.xcrossing.x, event.xcrossing.y, &event.xcrossing.x_root, &event.xcrossing.y_root, &subwin); XSendEvent(display, NUM2LONG(win), True, EnterWindowMask, &event); /* Parse keys */ tokens = strdup(RSTRING_PTR(keys)); tok = strtok_r(tokens, " ", &save); while(tok) { /* Parse key chain */ if(NoSymbol == (sym = subSharedParseKey(display, tok, &code, &state, &mouse))) { rb_raise(rb_eStandardError, "Unknown key"); return Qnil; } /* Check mouse */ if(True == mouse) { rb_raise(rb_eNotImpError, "Use #send_button instead"); return Qnil; } #ifdef HAVE_X11_EXTENSIONS_XTEST_H XTestGrabControl(display, True); /* Send key press/release events */ SubtlextSendModifier(state, True); XTestFakeKeyEvent(display, code, True, CurrentTime); XTestFakeKeyEvent(display, code, False, CurrentTime); SubtlextSendModifier(state, False); XTestGrabControl(display, False); #else /* HAVE_X11_EXTENSIONS_XTEST_H */ /* Send key press event */ event.type = KeyPress; event.xkey.state = state; event.xkey.keycode = code; XSendEvent(display, NUM2LONG(win), True, KeyPressMask, &event); XFlush(display); usleep(12000); /* Send key release event */ event.type = KeyRelease; XSendEvent(display, NUM2LONG(win), True, KeyReleaseMask, &event); #endif /* HAVE_X11_EXTENSIONS_XTEST_H */ tok = strtok_r(NULL, " ", &save); } XFlush(display); free(tokens); } else rb_raise(rb_eArgError, "Unexpected value-type `%s'", rb_obj_classname(keys)); return self; }
Add an existing tag to window
object.tag("subtle") => nil object.tag([ #<Subtlext::Tag:xxx>, #<Subtlext::Tag:xxx> ]) => nil object + "subtle" => nil
static VALUE SubtlextTagAdd(VALUE self, VALUE value) { return SubtlextTag(self, value, 1); }
Get list of tags for window
object.tags => [#<Subtlext::Tag:xxx>, #<Subtlext::Tag:xxx>]
static VALUE SubtlextTagReader(VALUE self) { char **tags = NULL; int i, ntags = 0, value_tags = 0; VALUE method = Qnil, klass = Qnil, t = Qnil; VALUE array = rb_ary_new(); /* Check ruby object */ rb_check_frozen(self); /* Fetch data */ method = rb_intern("new"); klass = rb_const_get(mod, rb_intern("Tag")); value_tags = FIX2INT(rb_iv_get(self, "@tags")); /* Check results */ if((tags = subSharedPropertyGetStrings(display, ROOT, XInternAtom(display, "SUBTLE_TAG_LIST", False), &ntags))) { for(i = 0; i < ntags; i++) { if(value_tags & (1L << (i + 1))) { /* Create new tag */ t = rb_funcall(klass, method, 1, rb_str_new2(tags[i])); rb_iv_set(t, "@id", INT2FIX(i)); rb_ary_push(array, t); } } XFreeStringList(tags); } return array; }
Set or remove all tags at once
# Set new tags object.tags=([ #<Subtlext::Tag:xxx>, #<Subtlext::Tag:xxx> ]) => nil # Remove all tags object.tags=([]) => nil
static VALUE SubtlextTagWriter(VALUE self, VALUE value) { return SubtlextTag(self, value, 0); }
Convert this Client object to string.
puts client => "subtle"
VALUE subextClientToString(VALUE self) { VALUE name = Qnil; /* Check ruby object */ GET_ATTR(self, "@name", name); return name; }
Toggle Client borderless state.
client.toggle_borderless => nil
VALUE subextClientFlagsToggleBorderless(VALUE self) { return ClientFlagsSet(self, SUB_EWMH_BORDERLESS, True); }
Toggle Client fixed state.
client.toggle_fixed => nil
VALUE subextClientFlagsToggleFixed(VALUE self) { return ClientFlagsSet(self, SUB_EWMH_FIXED, True); }
Toggle Client floating state.
client.toggle_float => nil
VALUE subextClientFlagsToggleFloat(VALUE self) { return ClientFlagsToggle(self, "_NET_WM_STATE_ABOVE", SUB_EWMH_FLOAT); }
Toggle Client fullscreen state.
client.toggle_full => nil
VALUE subextClientFlagsToggleFull(VALUE self) { return ClientFlagsToggle(self, "_NET_WM_STATE_FULLSCREEN", SUB_EWMH_FULL); }
Toggle Client resize state.
client.toggle_stick => nil
VALUE subextClientFlagsToggleResize(VALUE self) { return ClientFlagsSet(self, SUB_EWMH_RESIZE, True); }
Toggle Client sticky state.
client.toggle_stick => nil
VALUE subextClientFlagsToggleStick(VALUE self) { return ClientFlagsToggle(self, "_NET_WM_STATE_STICKY", SUB_EWMH_STICK); }
Toggle Client urgent state.
client.toggle_urgent => nil
VALUE subextClientFlagsToggleUrgent(VALUE self) { return ClientFlagsSet(self, SUB_EWMH_URGENT, True); }
Toggle Client zaphod state.
client.toggle_zaphod => nil
VALUE subextClientFlagsToggleZaphod(VALUE self) { return ClientFlagsSet(self, SUB_EWMH_ZAPHOD, True); }
Remove an existing tag from window
object.untag("subtle") => nil object.untag([ #<Subtlext::Tag:xxx>, #<Subtlext::Tag:xxx> ]) => nil object - "subtle" => nil
static VALUE SubtlextTagDel(VALUE self, VALUE value) { return SubtlextTag(self, value, -1); }
VALUE subextClientUpdate(VALUE self) { Window win = None; rb_check_frozen(self); subextSubtlextConnect(NULL); ///< Implicit open connection /* Check values */ if(0 <= (win = NUM2LONG(rb_iv_get(self, "@win")))) { int *tags = NULL, *flags = NULL; char *wmname = NULL, *wminstance = NULL, *wmclass = NULL, *role = NULL; /* Fetch name, instance and class */ subSharedPropertyClass(display, win, &wminstance, &wmclass); subSharedPropertyName(display, win, &wmname, wmclass); /* Fetch tags, flags and role */ tags = (int *)subSharedPropertyGet(display, win, XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_TAGS", False), NULL); flags = (int *)subSharedPropertyGet(display, win, XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_FLAGS", False), NULL); role = subSharedPropertyGet(display, win, XA_STRING, XInternAtom(display, "WM_WINDOW_ROLE", False), NULL); /* Set properties */ rb_iv_set(self, "@tags", tags ? INT2FIX(*tags) : INT2FIX(0)); rb_iv_set(self, "@flags", flags ? INT2FIX(*flags) : INT2FIX(0)); rb_iv_set(self, "@name", rb_str_new2(wmname)); rb_iv_set(self, "@instance", rb_str_new2(wminstance)); rb_iv_set(self, "@klass", rb_str_new2(wmclass)); rb_iv_set(self, "@role", role ? rb_str_new2(role) : Qnil); /* Set to nil for on demand loading */ rb_iv_set(self, "@geometry", Qnil); rb_iv_set(self, "@gravity", Qnil); if(tags) free(tags); if(flags) free(flags); if(role) free(role); free(wmname); free(wminstance); free(wmclass); } else rb_raise(rb_eStandardError, "Invalid client id `%#lx'", win); return self; }
Get array of Views Client is visible.
client.views => [#<Subtlext::View:xxx>, #<Subtlext::View:xxx>] client.views => nil
VALUE subextClientViewList(VALUE self) { int i, nnames = 0; char **names = NULL; VALUE win = Qnil, array = Qnil, method = Qnil, klass = Qnil; unsigned long *view_tags = NULL, *client_tags = NULL, *flags = NULL; /* Check ruby object */ rb_check_frozen(self); GET_ATTR(self, "@win", win); subextSubtlextConnect(NULL); ///< Implicit open connection /* Fetch data */ method = rb_intern("new"); klass = rb_const_get(mod, rb_intern("View")); array = rb_ary_new(); names = subSharedPropertyGetStrings(display, DefaultRootWindow(display), XInternAtom(display, "_NET_DESKTOP_NAMES", False), &nnames); view_tags = (unsigned long *)subSharedPropertyGet(display, DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display, "SUBTLE_VIEW_TAGS", False), NULL); client_tags = (unsigned long *)subSharedPropertyGet(display, NUM2LONG(win), XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_TAGS", False), NULL); flags = (unsigned long *)subSharedPropertyGet(display, NUM2LONG(win), XA_CARDINAL, XInternAtom(display, "SUBTLE_CLIENT_FLAGS", False), NULL); /* Check results */ if(names && view_tags && client_tags) { for(i = 0; i < nnames; i++) { /* Check if there are common tags or window is stick */ if((view_tags[i] & *client_tags) || (flags && *flags & SUB_EWMH_STICK)) { /* Create new view */ VALUE v = rb_funcall(klass, method, 1, rb_str_new2(names[i])); rb_iv_set(v, "@id", INT2FIX(i)); rb_ary_push(array, v); } } } if(names) XFreeStringList(names); if(view_tags) free(view_tags); if(client_tags) free(client_tags); if(flags) free(flags); return array; }