Class for interaction with views
Methods
Public Class
Public Instance
Public Class methods
Get currently active View.
Subtlext::View.current => #<Subtlext::View:xxx>
VALUE
subextViewSingCurrent(VALUE self)
{
int nnames = 0;
char **names = NULL;
long *tags = NULL;
unsigned long *cur_view = NULL;
VALUE view = Qnil;
subextSubtlextConnect(NULL); ///< Implicit open connection
/* Fetch data */
names = subSharedPropertyGetStrings(display, DefaultRootWindow(display),
XInternAtom(display, "_NET_DESKTOP_NAMES", False), &nnames);
cur_view = (unsigned long *)subSharedPropertyGet(display,
DefaultRootWindow(display), XA_CARDINAL,
XInternAtom(display, "_NET_CURRENT_DESKTOP", False), NULL);
tags = (long *)subSharedPropertyGet(display, ROOT, XA_CARDINAL,
XInternAtom(display, "SUBTLE_VIEW_TAGS", False), NULL);
/* Check results */
if(names && cur_view && tags)
{
/* Create instance */
view = subextViewInstantiate(names[*cur_view]);
rb_iv_set(view, "@id", INT2FIX(*cur_view));
rb_iv_set(view, "@tags", LONG2NUM(tags[*cur_view]));
}
if(names) XFreeStringList(names);
if(cur_view) free(cur_view);
if(tags) free(tags);
return view;
}
Find View by a given value which can be of following type:
| Fixnum |
Array index of the |
| String |
Regexp match against name of Views, returns a View on single match or an Array on multiple matches. |
| Symbol |
Either :current for current View, :all for an array of all Views or any string for an exact match. |
Subtlext::View.find(1)
=> [#<Subtlext::View:xxx>]
Subtlext::View.find("subtle")
=> [#<Subtlext::View:xxx>]
Subtlext::View[".*"]
=> [#<Subtlext::View:xxx>, #<Subtlext::View:xxx>]
Subtlext::View["subtle"]
=> []
Subtlext::View[:terms]
=> #<Subtlext::View:xxx>]
VALUE
subextViewSingFind(VALUE self,
VALUE value)
{
return ViewFind(value, False);
}
Find first View by a given value which can be of following type:
| Fixnum |
Array index of the |
| String |
Regexp match against name of Views, returns a View on single match or an Array on multiple matches. |
| Symbol |
Either :current for current View, :all for an array of all Views or any string for an exact match. |
Subtlext::View.first(1)
=> #<Subtlext::View:xxx>
Subtlext::View.first("subtle")
=> #<Subtlext::View:xxx>
VALUE
subextViewSingFirst(VALUE self,
VALUE value)
{
return ViewFind(value, True);
}
Get an array of all Views based on the _NET_DESKTOP_NAMES
property list.
Subtlext::View.list => [#<Subtlext::View:xxx>, #<Subtlext::View:xxx>] Subtlext::View.list => []
VALUE
subextViewSingList(VALUE self)
{
int i, nnames = 0;
long *tags = NULL;
char **names = NULL;
VALUE meth = Qnil, klass = Qnil, array = Qnil, v = Qnil;
subextSubtlextConnect(NULL); ///< Implicit open connection
/* Fetch data */
klass = rb_const_get(mod, rb_intern("View"));
meth = rb_intern("new");
array = rb_ary_new();
names = subSharedPropertyGetStrings(display, DefaultRootWindow(display),
XInternAtom(display, "_NET_DESKTOP_NAMES", False), &nnames);
tags = (long *)subSharedPropertyGet(display, ROOT, XA_CARDINAL,
XInternAtom(display, "SUBTLE_VIEW_TAGS", False), NULL);
/* Check results */
if(names && tags)
{
for(i = 0; i < nnames; i++)
{
if(!NIL_P(v = rb_funcall(klass, meth, 1, rb_str_new2(names[i]))))
{
rb_iv_set(v, "@id", INT2FIX(i));
rb_iv_set(v, "@tags", LONG2NUM(tags[i]));
rb_ary_push(array, v);
}
}
}
if(names) XFreeStringList(names);
if(tags) free(tags);
return array;
}
Create a new View object locally without calling save automatically.
The View won't be visible until save is called.
view = Subtlext::View.new("subtle")
=> #<Subtlext::View:xxx>
VALUE
subextViewInit(VALUE self,
VALUE name)
{
if(T_STRING != rb_type(name))
rb_raise(rb_eArgError, "Unexpected value-type `%s'",
rb_obj_classname(name));
/* Init object */
rb_iv_set(self, "@id", Qnil);
rb_iv_set(self, "@name", name);
rb_iv_set(self, "@tags", INT2FIX(0));
subextSubtlextConnect(NULL); ///< Implicit open connection
return self;
}
Get an array of all visible Views on connected Screens.
Subtlext::View.visible => [#<Subtlext::View:xxx>, #<Subtlext::View:xxx>] Subtlext::View.visible => []
VALUE
subextViewSingVisible(VALUE self)
{
int i, nnames = 0, *tags = NULL;
char **names = NULL;
unsigned long *visible = NULL;
VALUE meth = Qnil, klass = Qnil, array = Qnil, v = Qnil;
subextSubtlextConnect(NULL); ///< Implicit open connection
/* Fetch data */
meth = 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);
visible = (unsigned long *)subSharedPropertyGet(display,
DefaultRootWindow(display), XA_CARDINAL, XInternAtom(display,
"SUBTLE_VISIBLE_VIEWS", False), NULL);
tags = (int *)subSharedPropertyGet(display, ROOT, XA_CARDINAL,
XInternAtom(display, "SUBTLE_VIEW_TAGS", False), NULL);
/* Check results */
if(names && visible && tags)
{
for(i = 0; i < nnames; i++)
{
/* Create view on match */
if(*visible & (1L << (i + 1)) &&
!NIL_P(v = rb_funcall(klass, meth, 1, rb_str_new2(names[i]))))
{
rb_iv_set(v, "@id", INT2FIX(i));
rb_iv_set(v, "@tags", INT2FIX(tags[i]));
rb_ary_push(array, v);
}
}
}
if(names) XFreeStringList(names);
if(visible) free(visible);
if(tags) free(tags);
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 id)
object1 <=> object2 => 0
static VALUE
SubtlextEqualSpaceId(VALUE self,
VALUE other)
{
return SubtlextSpaceship(self, other, "@id");
}
Whether both objects have the same values (based on id)
object1 == object2 => true
static VALUE
SubtlextEqualId(VALUE self,
VALUE other)
{
return SubtlextEqual(self, other, "@id", 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;
}
Get an array of visible Clients on this View.
view.clients => [#<Subtlext::Client:xxx>, #<Subtlext::Client:xxx>] view.clients => []
VALUE
subextViewClients(VALUE self)
{
int i, nclients = 0;
Window *clients = NULL;
VALUE id = Qnil, klass = Qnil, meth = Qnil, array = Qnil, client = Qnil;
unsigned long *view_tags = NULL;
/* Check ruby object */
rb_check_frozen(self);
GET_ATTR(self, "@id", id);
subextSubtlextConnect(NULL); ///< Implicit open connection
/* Fetch data */
klass = rb_const_get(mod, rb_intern("Client"));
meth = rb_intern("new");
array = rb_ary_new();
clients = subextSubtlextWindowList("_NET_CLIENT_LIST", &nclients);
view_tags = (unsigned long *)subSharedPropertyGet(display,
DefaultRootWindow(display), XA_CARDINAL,
XInternAtom(display, "SUBTLE_VIEW_TAGS", False), NULL);
/* Check results */
if(clients && view_tags)
{
for(i = 0; i < nclients; i++)
{
unsigned long *client_tags = NULL, *flags = NULL;
/* Fetch window data */
client_tags = (unsigned long *)subSharedPropertyGet(display,
clients[i], XA_CARDINAL,
XInternAtom(display, "SUBTLE_CLIENT_TAGS", False), NULL);
flags = (unsigned long *)subSharedPropertyGet(display,
clients[i], XA_CARDINAL,
XInternAtom(display, "SUBTLE_CLIENT_FLAGS", False), NULL);
/* Check if there are common tags or window is stick */
if((client_tags && view_tags[FIX2INT(id)] & *client_tags) ||
(flags && *flags & SUB_EWMH_STICK))
{
if(RTEST(client = rb_funcall(klass, meth,
1, LONG2NUM(clients[i]))))
{
subextClientUpdate(client);
rb_ary_push(array, client);
}
}
if(client_tags) free(client_tags);
if(flags) free(flags);
}
}
if(clients) free(clients);
if(view_tags) free(view_tags);
return array;
}
VALUE
subextViewAskCurrent(VALUE self)
{
VALUE id = Qnil, ret = Qfalse;;
unsigned long *cur_view = NULL;
/* Check ruby object */
rb_check_frozen(self);
GET_ATTR(self, "@id", id);
/* Check results */
if((cur_view = (unsigned long *)subSharedPropertyGet(display,
DefaultRootWindow(display), XA_CARDINAL,
XInternAtom(display, "_NET_CURRENT_DESKTOP", False), NULL)))
{
if(FIX2INT(id) == *cur_view) ret = Qtrue;
free(cur_view);
}
return ret;
}
Whether both objects have the same values and types (based on id)
object1.eql? object2 => true
static VALUE
SubtlextEqualTypedId(VALUE self,
VALUE other)
{
return SubtlextEqual(self, other, "@id", True);
}
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;
}
VALUE
subextViewIcon(VALUE self)
{
unsigned long nicons = 0;
VALUE id = Qnil, ret = Qnil;
unsigned long *icons = NULL;
/* Check ruby object */
rb_check_frozen(self);
GET_ATTR(self, "@id", id);
subextSubtlextConnect(NULL); ///< Implicit open connection
/* Check results */
if((icons = (unsigned long *)subSharedPropertyGet(display,
DefaultRootWindow(display), XA_CARDINAL,
XInternAtom(display, "SUBTLE_VIEW_ICONS", False), &nicons)))
{
int iid = FIX2INT(id);
/* Check if id is in range and icon available */
if(0 <= iid && iid < nicons && -1 != icons[iid])
{
/* Create new icon */
ret = rb_funcall(rb_const_get(mod, rb_intern("Icon")),
rb_intern("new"), 1, LONG2NUM(icons[iid]));
}
free(icons);
}
return ret;
}
Set this View to the current active one
view.jump => #<Subtlext::View:xxx>
VALUE
subextViewJump(VALUE self)
{
VALUE id = Qnil;
SubMessageData data = { { 0, 0, 0, 0, 0 } };
/* Check ruby object */
rb_check_frozen(self);
GET_ATTR(self, "@id", id);
subextSubtlextConnect(NULL); ///< Implicit open connection
/* Send message */
data.l[0] = FIX2INT(id);
data.l[2] = -1;
subSharedMessage(display, DefaultRootWindow(display),
"_NET_CURRENT_DESKTOP", data, 32, True);
return self;
}
Remove this View from subtle and freeze this object.
view.kill => nil
VALUE
subextViewKill(VALUE self)
{
VALUE id = Qnil;
SubMessageData data = { { 0, 0, 0, 0, 0 } };
/* Check ruby object */
rb_check_frozen(self);
GET_ATTR(self, "@id", id);
subextSubtlextConnect(NULL); ///< Implicit open connection
/* Send message */
data.l[0] = FIX2INT(id);
subSharedMessage(display, DefaultRootWindow(display),
"SUBTLE_VIEW_KILL", data, 32, True);
rb_obj_freeze(self); ///< Freeze object
return Qnil;
}
Select next View, but doesn't cycle list.
view.next => #<Subtlext::View:xxx>
VALUE
subextViewSelectNext(VALUE self)
{
return ViewSelect(self, SUB_VIEW_NEXT);
}
Select prev View, but doesn't cycle list.
view.prev => #<Subtlext::View:xxx>
VALUE
subextViewSelectPrev(VALUE self)
{
return ViewSelect(self, SUB_VIEW_PREV);
}
Save new View object.
view.save => #<Subtlext::View:xxx>
VALUE
subextViewSave(VALUE self)
{
int id = -1;
VALUE name = Qnil;
/* Check ruby object */
rb_check_frozen(self);
GET_ATTR(self, "@name", name);
subextSubtlextConnect(NULL); ///< Implicit open connection
/* Create view if needed */
if(-1 == (id = subextSubtlextFindString("_NET_DESKTOP_NAMES",
RSTRING_PTR(name), NULL, SUB_MATCH_EXACT)))
{
SubMessageData data = { { 0, 0, 0, 0, 0 } };
snprintf(data.b, sizeof(data.b), "%s", RSTRING_PTR(name));
subSharedMessage(display, DefaultRootWindow(display),
"SUBTLE_VIEW_NEW", data, 8, True);
id = subextSubtlextFindString("_NET_DESKTOP_NAMES",
RSTRING_PTR(name), NULL, SUB_MATCH_EXACT);
}
/* Guess view id */
if(-1 == id)
{
int nnames = 0;
char **names = NULL;
/* Get names of views */
if((names = subSharedPropertyGetStrings(display, DefaultRootWindow(display),
XInternAtom(display, "_NET_DESKTOP_NAMES", False), &nnames)))
{
id = nnames; ///< New id should be last
if(names) XFreeStringList(names);
}
}
/* Set properties */
rb_iv_set(self, "@id", INT2FIX(id));
return self;
}
Set style state of this Object, use nil to reset state.
object.style = :blue => nil
static VALUE
SubtlextStyle(VALUE self,
VALUE value)
{
char *prop = NULL;
VALUE id = Qnil, str = Qnil;
SubMessageData data = { { 0, 0, 0, 0, 0 } };
/* Check ruby object */
rb_check_frozen(self);
GET_ATTR(self, "@id", id);
/* Check object type */
if(rb_obj_is_instance_of(self, rb_const_get(mod, rb_intern("View"))))
prop = "SUBTLE_VIEW_STYLE";
else prop = "SUBTLE_SUBLET_STYLE";
/* Check value type */
switch(rb_type(value))
{
case T_SYMBOL: str = rb_sym_to_s(value);
case T_STRING:
snprintf(data.b, sizeof(data.b), "%d#%s",
(int)FIX2INT(id), RSTRING_PTR(str));
subSharedMessage(display, ROOT, prop, data, 32, True);
break;
case T_NIL:
snprintf(data.b, sizeof(data.b), "%d#", (int)FIX2INT(id));
subSharedMessage(display, ROOT, prop, data, 32, True);
break;
default: rb_raise(rb_eArgError, "Unexpected value-type `%s'",
rb_obj_classname(value));
}
return Qnil;
}
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 View object to string.
puts view => "subtle"
VALUE
subextViewToString(VALUE self)
{
VALUE name = Qnil;
/* Check ruby object */
GET_ATTR(self, "@name", name);
return name;
}
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
subextViewUpdate(VALUE self)
{
long *tags = NULL, ntags = 0;
VALUE id = Qnil;
/* Check ruby object */
rb_check_frozen(self);
GET_ATTR(self, "@id", id);
subextSubtlextConnect(NULL); ///< Implicit open connection
/* Fetch tags */
if((tags = (long *)subSharedPropertyGet(display, ROOT, XA_CARDINAL,
XInternAtom(display, "SUBTLE_VIEW_TAGS", False), (unsigned long *)&ntags)))
{
int idx = FIX2INT(id);
rb_iv_set(self, "@tags", LONG2NUM(idx < ntags ? tags[idx] : 0));
free(tags);
}
return self;
}