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;
}