Class Subtlext::Tray < Object

Class for interaction with trays

Attributes

instance [R] Instance of WM_CLASS
klass [R] Class of WM_CLASS
name [R] WM_NAME
win [R] Window id

Public class methods

find(value) → Array
[value] → Array

Find Tray by a given value which can be of following type:

Fixnum
Array index of the SUBTLE_TRAY_LIST property list.
String
Regexp match against both WM_CLASS values.
Hash
Instead of just match WM_CLASS match against following properties:
:name
Match against WM_NAME
:instance
Match against first value of WM_NAME
:class
Match against second value of WM_NAME
Symbol
Either :all for an array of all Trays or any string for an exact match.
 1 Subtlext::Tray.find(1)
 2 => [#<Subtlext::Tray:xxx>]
 3 
 4 Subtlext::Tray.find("subtle")
 5 => [#<Subtlext::Tray:xxx>]
 6 
 7 Subtlext::Tray[".*"]
 8 => [#<Subtlext::Tray:xxx>, #<Subtlext::Tray:xxx>]
 9 
10 Subtlext::Tray["subtle"]
11 => []
12 
13 Subtlext::Tray[:terms]
14 => [#<Subtlext::Tray:xxx>]
15 
16 Subtlext::Tray[name: "subtle"]
17 => [#<Subtlext::Tray:xxx>]
[show source]
VALUE
subextTraySingFind(VALUE self,
  VALUE value)
{
  return TrayFind(value, False);
}
find(value) → Subtlext::Tray or nil

Find first Tray by a given value which can be of following type:

Fixnum
Array index of the SUBTLE_TRAY_LIST property list.
String
Regexp match against both WM_CLASS values.
Hash
Instead of just match WM_CLASS match against following properties:
:name
Match against WM_NAME
:instance
Match against first value of WM_NAME
:class
Match against second value of WM_NAME
Symbol
Either :all for an array of all Trays or any string for an exact match.
1 Subtlext::Tray.first(1)
2 => #<Subtlext::Tray:xxx>
3 
4 Subtlext::Tray.first("subtle")
5 => #<Subtlext::Tray:xxx>
[show source]
VALUE
subextTraySingFirst(VALUE self,
  VALUE value)
{
  return TrayFind(value, True);
}
list → Array

Get an array of all Trays based on SUBTLE_TRAY_LIST property list.

1 Subtlext::Tray.list
2 => [#<Subtlext::Tray:xxx>, #<Subtlext::Tray:xxx>]
3 
4 Subtlext::Tray.list
5 => []
[show source]
VALUE
subextTraySingList(VALUE self)
{
  int i, ntrays = 0;
  Window *trays = NULL;
  VALUE meth = Qnil, klass = Qnil, array = Qnil;

  subextSubtlextConnect(NULL); ///< Implicit open connection

  /* Fetch data */
  meth  = rb_intern("new");
  klass = rb_const_get(mod, rb_intern("Tray"));
  array = rb_ary_new();

  /* Check results */
  if((trays = subextSubtlextWindowList("SUBTLE_TRAY_LIST", &ntrays)))
    {
      for(i = 0; i < ntrays; i++)
        {
          VALUE t = rb_funcall(klass, meth, 1, LONG2NUM(trays[i]));

          if(!NIL_P(t)) subextTrayUpdate(t);

          rb_ary_push(array, t);
        }

      free(trays);
    }

  return array;
}
new(name) → Subtlext::Tray

Create a new Tray object locally without calling save automatically.

The Tray won’t be visible until save is called.

1 tag = Subtlext::Tray.new("subtle")
2 => #<Subtlext::Tray:xxx>
[show source]
VALUE
subextTrayInit(VALUE self,
  VALUE win)
{
  if(!FIXNUM_P(win) && T_BIGNUM != rb_type(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, "@klass", Qnil);
  rb_iv_set(self, "@title", Qnil);

  subextSubtlextConnect(NULL); ///< Implicit open connection

  return self;
}

Public instance methods

<=>(other) → -1, 0 or 1

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)

1 object1 <=> object2
2 => 0
[show source]
static VALUE
SubtlextEqualSpaceWindow(VALUE self,
  VALUE other)
{
  return SubtlextSpaceship(self, other, "@win");
}
==(other) → True or False

Whether both objects have the same values (based on win)

1 object1 == object2
2 => true
[show source]
static VALUE
SubtlextEqualWindow(VALUE self,
  VALUE other)
{
  return SubtlextEqual(self, other, "@win", False);
}
[value] → String or Nil

Get arbitrary persistent property string or symbol value

1 object["wm"]
2 => "subtle"
3 
4 object[:wm]
5 => "subtle"
[show source]
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;
}
[key]= value → Nil

Set arbitrary persistent property string or symbol value

Symbols are implictly converted to string, to remove a property just set it to nil.

1 object["wm"] = "subtle"
2 => nil
3 
4 object[:wm] = "subtle"
5 => nil
6 
7 object[:wm] = nil
8 => nil
[show source]
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;
}
click (...)

Alias for send_button

eql?(other) → True or False

Whether both objects have the same value and types (based on win)

1 object1.eql? object2
2 => true
[show source]
static VALUE
SubtlextEqualTypedWindow(VALUE self,
  VALUE other)
{
  return SubtlextEqual(self, other, "@win", True);
}
focus → nil

Set focus to window

1 object.focus
2 => nil
[show source]
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;
}
has_focus? → true or false

Check if window has focus

1 object.focus?
2 => true
3 
4 object.focus?
5 => false
[show source]
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;
}
hash → Hash

Convert this object to hash.

1 puts object.hash
2 => 1746246187916025425
[show source]
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;
}
kill → nil

Send a close signal to Client and freeze this object.

1 tray.kill
2 => nil
[show source]
VALUE
subextTrayKill(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;
}
pid => Fixnum

Get window pid

1 object.pid
2 => 123
[show source]
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;
}
send_button(button, x, y) → Object

Emulate a click on a window with optional button and x/y position

1 object.send_button
2 => nil
3 
4 object.send_button(2)
5 => Object
[show source]
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;
}
send_key(key, x, y) → Object

Emulate a keypress on a window

1 object.send_key("d")
2 => Object
[show source]
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;
}
to_s ()

Alias for to_str

to_str → String

Convert this Tray object to string.

1 puts tray
2 => "subtle"
[show source]
VALUE
subextTrayToString(VALUE self)
{
  VALUE name = Qnil;

  /* Check ruby object */
  GET_ATTR(self, "@name", name);

  return name;
}
update → Subtlext::Tray

Update Tray properties based on required Tray window id.

1 tray.update
2 => #<Subtlext::Tray:xxx>
[show source]
VALUE
subextTrayUpdate(VALUE self)
{
  Window win = None;

  /* Check ruby object */
  rb_check_frozen(self);
  subextSubtlextConnect(NULL); ///< Implicit open connection

  /* Get tray values */
  win = NUM2LONG(rb_iv_get(self, "@win"));

  /* Check values */
  if(0 <= win)
    {
      char *wmname = NULL, *wminstance = NULL, *wmclass = NULL;

      /* Get name, instance and class */
      subSharedPropertyClass(display, win, &wminstance, &wmclass);
      subSharedPropertyName(display, win, &wmname, wmclass);

      /* Set properties */
      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));

      free(wmname);
      free(wminstance);
      free(wmclass);
    }
  else rb_raise(rb_eStandardError, "Invalid tray id `%#lx`", win);

  return self;
}