active_window_x 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +25 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +60 -0
- data/Rakefile +47 -0
- data/active_window_x.gemspec +22 -0
- data/ext/active_window_x/extconf.rb +7 -0
- data/ext/active_window_x/xlib.c +751 -0
- data/lib/active_window_x/atom.rb +30 -0
- data/lib/active_window_x/client_message_event.rb +44 -0
- data/lib/active_window_x/display.rb +79 -0
- data/lib/active_window_x/event.rb +15 -0
- data/lib/active_window_x/event_listener.rb +119 -0
- data/lib/active_window_x/property_event.rb +46 -0
- data/lib/active_window_x/root_window.rb +20 -0
- data/lib/active_window_x/version.rb +3 -0
- data/lib/active_window_x/window.rb +123 -0
- data/lib/active_window_x/xid.rb +29 -0
- data/lib/active_window_x.rb +30 -0
- data/sample/active_window_dump.rb +30 -0
- data/sample/simple.rb +6 -0
- data/spec/atom_spec.rb +27 -0
- data/spec/display_spec.rb +63 -0
- data/spec/root_window_spec.rb +47 -0
- data/spec/window_spec.rb +285 -0
- data/spec/xlib_spec.rb +245 -0
- metadata +136 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
module ActiveWindowX
|
4
|
+
|
5
|
+
# binding for Atom on X11
|
6
|
+
class Atom < Xid
|
7
|
+
@@cache = {}
|
8
|
+
|
9
|
+
def initialize display, second
|
10
|
+
if second.kind_of? Numeric
|
11
|
+
super
|
12
|
+
elsif second.kind_of? String
|
13
|
+
super
|
14
|
+
@id = display.intern_atom second
|
15
|
+
if @id == Xlib::None
|
16
|
+
raise ArgumentError, 'invalid an atom name: #{second}'
|
17
|
+
end
|
18
|
+
else
|
19
|
+
raise ArgumentError, 'expect Numeric or String with the second argument'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
alias :intern :id
|
24
|
+
|
25
|
+
def name
|
26
|
+
@display.atom_name @id
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
module ActiveWindowX
|
4
|
+
|
5
|
+
# binding for XClientMessageEvent on X11
|
6
|
+
class ClientMessageEvent < Event
|
7
|
+
|
8
|
+
# the number of last request processed by server
|
9
|
+
attr_reader :serial
|
10
|
+
|
11
|
+
# true if this came from a SendEvent request
|
12
|
+
attr_reader :send_event
|
13
|
+
|
14
|
+
# Display the event was read from
|
15
|
+
attr_reader :display
|
16
|
+
|
17
|
+
# the window whose associated property was changed
|
18
|
+
attr_reader :window
|
19
|
+
|
20
|
+
# an atom that indicates how the data should be interpreted
|
21
|
+
# by the receiving client
|
22
|
+
attr_reader :message_type
|
23
|
+
|
24
|
+
# 8, 16, or 32 and specifies whether the data should be viewed
|
25
|
+
# as a list of bytes, shorts, or longs
|
26
|
+
attr_reader :format
|
27
|
+
|
28
|
+
# a union that contains the members b, s, and l. The b, s, and l members
|
29
|
+
# represent data of twenty 8-bit values, ten 16-bit values,
|
30
|
+
# and five 32-bit values
|
31
|
+
attr_reader :data
|
32
|
+
|
33
|
+
def initialize display, raw
|
34
|
+
super
|
35
|
+
@serial = raw.serial
|
36
|
+
@send_event = (raw.send_event != 0)
|
37
|
+
@display = display
|
38
|
+
@window = Window.new display, raw.window
|
39
|
+
@message_type = Atom.new display, raw.message_type
|
40
|
+
@format = raw.message_type
|
41
|
+
@data = raw.data
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
module ActiveWindowX
|
4
|
+
|
5
|
+
# binding for Display on X11
|
6
|
+
class Display
|
7
|
+
|
8
|
+
# raw class of Display
|
9
|
+
attr_reader :raw
|
10
|
+
|
11
|
+
# a boolean which be true if this display was closed
|
12
|
+
attr_reader :closed
|
13
|
+
alias :closed? :closed
|
14
|
+
|
15
|
+
def initialize arg=nil
|
16
|
+
@raw =
|
17
|
+
if arg.nil? or arg.kind_of? String
|
18
|
+
Xlib::x_open_display arg
|
19
|
+
elsif arg.kind_of? Xlib::Display
|
20
|
+
arg
|
21
|
+
else
|
22
|
+
raise ArgumentError, 'expect nil, String or Xlib::Display'
|
23
|
+
end
|
24
|
+
@closed = false
|
25
|
+
@root_window = nil
|
26
|
+
@cache = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def close
|
30
|
+
Xlib::x_close_display @raw
|
31
|
+
@closed = true
|
32
|
+
end
|
33
|
+
|
34
|
+
def root_window
|
35
|
+
@root_window ||= RootWindow.new(self, Xlib::default_root_window(@raw))
|
36
|
+
end
|
37
|
+
|
38
|
+
# return IO to select and poll a XEvent with timeout
|
39
|
+
def connection
|
40
|
+
@conn ||= IO.new(Xlib::connection_number @raw)
|
41
|
+
end
|
42
|
+
|
43
|
+
# return the number of events that have been received from the X server
|
44
|
+
def pending
|
45
|
+
Xlib::x_pending @raw
|
46
|
+
end
|
47
|
+
|
48
|
+
def active_window
|
49
|
+
root_window.active_window
|
50
|
+
end
|
51
|
+
|
52
|
+
def intern_atom name
|
53
|
+
if @cache.has_key? name
|
54
|
+
@cache[name]
|
55
|
+
else
|
56
|
+
@cache[name] = Xlib::x_intern_atom @raw, name, false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def atom_name id
|
61
|
+
if @cache.has_key? id
|
62
|
+
@cache[id]
|
63
|
+
else
|
64
|
+
@cache[id] = Xlib::x_get_atom_name @raw, id
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def next_event
|
69
|
+
xevent = Xlib::x_next_event @raw
|
70
|
+
|
71
|
+
case xevent.type
|
72
|
+
when Xlib::PropertyNotify; PropertyEvent.new self, xevent
|
73
|
+
when Xlib::ClientMessage; ClientMessageEvent.new self, xevent
|
74
|
+
else Event.new self, xevent
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
module ActiveWindowX
|
4
|
+
|
5
|
+
# listen event changing active window
|
6
|
+
class EventListener
|
7
|
+
|
8
|
+
DEFAULT_TIMEOUT = 0.5
|
9
|
+
|
10
|
+
# current active window
|
11
|
+
attr_reader :active_window
|
12
|
+
|
13
|
+
# true if #start loop continued
|
14
|
+
# false if #start loop did not continue
|
15
|
+
# nil if #start loop was not started
|
16
|
+
# set false if you want to terminate #start loop when next timeout or event receiving
|
17
|
+
attr_accessor :continue
|
18
|
+
|
19
|
+
def initialize name=nil, timeout=DEFAULT_TIMEOUT, &block
|
20
|
+
@display = Display.new name
|
21
|
+
@default_timeout = timeout
|
22
|
+
|
23
|
+
@root = @display.root_window
|
24
|
+
@aw_atom = Atom.new @display, '_NET_ACTIVE_WINDOW'
|
25
|
+
@name_atom = Atom.new @display, 'WM_NAME'
|
26
|
+
@delete_atom = Atom.new @display, 'WM_DELETE_WINDOW'
|
27
|
+
@conn = @display.connection
|
28
|
+
@active_window = @root.active_window
|
29
|
+
|
30
|
+
@active_window.select_input Xlib::PropertyChangeMask if @active_window
|
31
|
+
@root.select_input Xlib::PropertyChangeMask
|
32
|
+
|
33
|
+
if block_given?
|
34
|
+
start @default_timeout, &block
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# event listener loop
|
39
|
+
#
|
40
|
+
# ActiveWindowX::EventListener.new.start do |e|
|
41
|
+
# puts e.type, e.window.id
|
42
|
+
# end
|
43
|
+
def start timeout=@default_timeout
|
44
|
+
|
45
|
+
if not block_given?
|
46
|
+
raise ArgumentError, 'expect to give a block'
|
47
|
+
end
|
48
|
+
|
49
|
+
@continue = true
|
50
|
+
begin
|
51
|
+
while @continue
|
52
|
+
event = listen timeout
|
53
|
+
next if not event
|
54
|
+
|
55
|
+
if window_closed?(event.window)
|
56
|
+
event.window = @root.active_window
|
57
|
+
end
|
58
|
+
yield event if event.type
|
59
|
+
end
|
60
|
+
ensure
|
61
|
+
destroy
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def destroy
|
66
|
+
@display.close if @display.closed?
|
67
|
+
end
|
68
|
+
|
69
|
+
# receive a event
|
70
|
+
#
|
71
|
+
# return value:
|
72
|
+
# a ActiveWindowX::EventListener::Event if an event was send within _timeout_ sec
|
73
|
+
# nil if timeout
|
74
|
+
def listen timeout=nil
|
75
|
+
if @display.pending == 0 and
|
76
|
+
select([@conn], [], [], timeout) == nil
|
77
|
+
# ope on timeout
|
78
|
+
return nil
|
79
|
+
end
|
80
|
+
|
81
|
+
type = nil
|
82
|
+
active_window = nil
|
83
|
+
|
84
|
+
event = @display.next_event
|
85
|
+
if event.atom == @aw_atom
|
86
|
+
type = :active_window
|
87
|
+
active_window = @root.active_window
|
88
|
+
elsif event.atom == @name_atom
|
89
|
+
type = :title
|
90
|
+
active_window = event.window
|
91
|
+
end
|
92
|
+
|
93
|
+
if type == :active_window and @active_window != active_window
|
94
|
+
@active_window.select_input(Xlib::NoEventMask) if @active_window
|
95
|
+
@active_window = active_window
|
96
|
+
@active_window.select_input(Xlib::PropertyChangeMask) if @active_window
|
97
|
+
end
|
98
|
+
|
99
|
+
Event.new type, active_window
|
100
|
+
end
|
101
|
+
|
102
|
+
def window_closed? w
|
103
|
+
return false if w.nil?
|
104
|
+
begin
|
105
|
+
w.prop_raw 'WM_STATE'
|
106
|
+
false
|
107
|
+
rescue Xlib::XErrorEvent
|
108
|
+
true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class Event
|
113
|
+
attr_accessor :type, :window
|
114
|
+
def initialize type, window
|
115
|
+
@type = type; @window = window
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
module ActiveWindowX
|
4
|
+
|
5
|
+
# binding for XPropertyEvent on X11
|
6
|
+
class PropertyEvent < Event
|
7
|
+
|
8
|
+
# the number of last request processed by server
|
9
|
+
attr_reader :serial
|
10
|
+
|
11
|
+
# true if this came from a SendEvent request
|
12
|
+
attr_reader :send_event
|
13
|
+
|
14
|
+
# Display the event was read from
|
15
|
+
attr_reader :display
|
16
|
+
|
17
|
+
# the window whose associated property was changed
|
18
|
+
attr_reader :window
|
19
|
+
|
20
|
+
# the property's atom and indicates which property was changed or desired
|
21
|
+
attr_reader :atom
|
22
|
+
|
23
|
+
# the server time when the property was changed
|
24
|
+
attr_reader :time
|
25
|
+
|
26
|
+
# * PropertyNewValue when a property of the window is changed using
|
27
|
+
# XChangeProperty or XRotateWindowProperties (even when adding zero-length
|
28
|
+
# data using XChangeProperty) and when replacing all or part of a property
|
29
|
+
# with identical data using XChangeProperty or XRotateWindowProperties.
|
30
|
+
# * PropertyDelete when a property of the window is deleted using
|
31
|
+
# XDeleteProperty or, if the delete argument is True, XGetWindowProperty
|
32
|
+
attr_reader :state
|
33
|
+
|
34
|
+
def initialize display, raw
|
35
|
+
super
|
36
|
+
@serial = raw.serial
|
37
|
+
@send_event = (raw.send_event != 0)
|
38
|
+
@display = display
|
39
|
+
@window = Window.new display, raw.window
|
40
|
+
@atom = Atom.new display, raw.atom
|
41
|
+
@time = raw.time
|
42
|
+
@state = raw.state
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
require "active_window_x/window"
|
4
|
+
|
5
|
+
module ActiveWindowX
|
6
|
+
|
7
|
+
# binding for a root Window on X11
|
8
|
+
class RootWindow < Window
|
9
|
+
|
10
|
+
def active_window
|
11
|
+
prop_val = prop '_NET_ACTIVE_WINDOW'
|
12
|
+
if prop_val.nil? or prop_val.first == Xlib::None
|
13
|
+
nil
|
14
|
+
else
|
15
|
+
Window.new(@display, prop_val.first)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
module ActiveWindowX
|
4
|
+
|
5
|
+
# binding for Window on X11
|
6
|
+
class Window < Xid
|
7
|
+
|
8
|
+
# a buffer for #x_get_window_property
|
9
|
+
READ_BUFF_LENGTH = 1024
|
10
|
+
|
11
|
+
def x_query_tree
|
12
|
+
Xlib::x_query_tree @display.raw, @id
|
13
|
+
end
|
14
|
+
|
15
|
+
# a return value of XQueryTree
|
16
|
+
# which is the root window for a display contains this window
|
17
|
+
def root
|
18
|
+
(r = x_query_tree[0]) and Window.new(@display, r)
|
19
|
+
end
|
20
|
+
|
21
|
+
# a return value of XQueryTree
|
22
|
+
# which is nil, if this window is RootWindow, or a Window.
|
23
|
+
def parent
|
24
|
+
(r = x_query_tree[1]) and Window.new(@display, r)
|
25
|
+
end
|
26
|
+
|
27
|
+
# a return value of XQueryTree
|
28
|
+
# which is an Array of Window
|
29
|
+
def children
|
30
|
+
x_query_tree[2].map{|w| Window.new(@display, w)}
|
31
|
+
end
|
32
|
+
|
33
|
+
# window title (current web page title in browser, current command or dir in terminal app, etc.)
|
34
|
+
def title
|
35
|
+
title = prop('_NET_WM_NAME')
|
36
|
+
title or prop('WM_NAME')
|
37
|
+
end
|
38
|
+
|
39
|
+
# window name (terminal, google-chrome, etc.)
|
40
|
+
def app_name
|
41
|
+
val = app_class_prop
|
42
|
+
val and val[0]
|
43
|
+
end
|
44
|
+
|
45
|
+
# window class (Terminal, Google-chrome, etc.)
|
46
|
+
# TODO write the difference of app_name and app_class
|
47
|
+
def app_class
|
48
|
+
val = app_class_prop
|
49
|
+
val and val[1]
|
50
|
+
end
|
51
|
+
|
52
|
+
def app_class_prop
|
53
|
+
val = prop('WM_CLASS')
|
54
|
+
val and val.split("\0")
|
55
|
+
end
|
56
|
+
|
57
|
+
def pid
|
58
|
+
val = prop('_NET_WM_PID')
|
59
|
+
val and val.first
|
60
|
+
end
|
61
|
+
|
62
|
+
def command
|
63
|
+
id = pid
|
64
|
+
return nil if id.nil?
|
65
|
+
|
66
|
+
path = "/proc/#{id}/cmdline"
|
67
|
+
return nil unless File.readable_real? path
|
68
|
+
|
69
|
+
File.read path
|
70
|
+
end
|
71
|
+
|
72
|
+
# window property getter with easy way for XGetWindowProperty
|
73
|
+
# which return nil, if the specified property name does not exist,
|
74
|
+
# a String or a Array of Number
|
75
|
+
def prop atom
|
76
|
+
val, format, nitems = prop_raw atom
|
77
|
+
case format
|
78
|
+
when 32; val.unpack("l!#{nitems}")
|
79
|
+
when 16; val.unpack("s#{nitems}")
|
80
|
+
when 8; val[0, nitems]
|
81
|
+
when 0; nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# window property getter with easy way for XGetWindowProperty
|
86
|
+
# which return [propety_value, format, number_of_items]
|
87
|
+
def prop_raw atom
|
88
|
+
if atom.kind_of?(Numeric) or atom.kind_of?(String)
|
89
|
+
atom = Atom.new @display, atom
|
90
|
+
elsif not atom.kind_of? Atom
|
91
|
+
raise ArgumentError, "expect Numeric, String or #{Atom.name}"
|
92
|
+
end
|
93
|
+
actual_type, actual_format, nitems, bytes_after, val =
|
94
|
+
Xlib::x_get_window_property @display.raw, @id, atom.id, 0, READ_BUFF_LENGTH, false, Xlib::AnyPropertyType
|
95
|
+
return [val, actual_format, nitems]
|
96
|
+
end
|
97
|
+
|
98
|
+
# Array of the property atom ID(Numeric) list for this window
|
99
|
+
def prop_atom_ids
|
100
|
+
r = Xlib::x_list_properties @display.raw, @id
|
101
|
+
r.nil? ? [] : r
|
102
|
+
end
|
103
|
+
|
104
|
+
# Array of the property atom list for this window
|
105
|
+
def prop_atoms
|
106
|
+
prop_atom_ids.map{|i| Atom.new @display, i}
|
107
|
+
end
|
108
|
+
|
109
|
+
def select_input mask
|
110
|
+
Xlib::x_select_input @display.raw, @id, mask
|
111
|
+
end
|
112
|
+
|
113
|
+
def set_wm_protocols msgs
|
114
|
+
atoms =
|
115
|
+
if msgs.kind_of? Atom then [msgs.id]
|
116
|
+
elsif msgs.kind_of? Array then msgs.map {|m| m.id }
|
117
|
+
else raise ArgumentError, 'expect Atom or Array of Atom'
|
118
|
+
end
|
119
|
+
Xlib::x_set_wm_protocols @display.raw, @id, atoms
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
module ActiveWindowX
|
4
|
+
|
5
|
+
# binding for XID on X11
|
6
|
+
class Xid
|
7
|
+
# a display which has this XID
|
8
|
+
attr_reader :display
|
9
|
+
|
10
|
+
# raw XID (#define Window unsinged long)
|
11
|
+
attr_reader :id
|
12
|
+
|
13
|
+
def initialize display, id
|
14
|
+
if display.kind_of? Display
|
15
|
+
@display = display
|
16
|
+
elsif display.kind_of? Xlib::Display
|
17
|
+
@display = Display.new display
|
18
|
+
else
|
19
|
+
raise ArgumentError, "expect #{Display.name} or #{Xlib::Display.name}"
|
20
|
+
end
|
21
|
+
@id ||= id
|
22
|
+
end
|
23
|
+
|
24
|
+
def == xid
|
25
|
+
xid.kind_of?(Xid) and (xid.id == @id)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- coding:undecided-unix; mode:ruby; -*-
|
2
|
+
|
3
|
+
require "active_window_x/version"
|
4
|
+
require "active_window_x/xlib"
|
5
|
+
require "active_window_x/display"
|
6
|
+
require "active_window_x/xid"
|
7
|
+
require "active_window_x/window"
|
8
|
+
require "active_window_x/root_window"
|
9
|
+
require "active_window_x/atom"
|
10
|
+
require "active_window_x/event"
|
11
|
+
require "active_window_x/property_event"
|
12
|
+
require "active_window_x/client_message_event"
|
13
|
+
require "active_window_x/event_listener"
|
14
|
+
|
15
|
+
module ActiveWindowX
|
16
|
+
module Xlib; end
|
17
|
+
|
18
|
+
class Display; end
|
19
|
+
|
20
|
+
class XID; end
|
21
|
+
class Window < Xid; end
|
22
|
+
class RootWindow < Window; end
|
23
|
+
class Atom < Xid; end
|
24
|
+
|
25
|
+
class Event; end
|
26
|
+
class PropertyEvent < Event; end
|
27
|
+
class ClientMessageEvent < Event; end
|
28
|
+
|
29
|
+
class EventListener; end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# dump all properties of window on changing active window
|
2
|
+
|
3
|
+
require 'active_window_x'
|
4
|
+
|
5
|
+
@listener = ActiveWindowX::EventListener.new
|
6
|
+
|
7
|
+
# when pressing Ctrl-C
|
8
|
+
trap :INT do
|
9
|
+
@listener.destroy
|
10
|
+
exit true
|
11
|
+
end
|
12
|
+
|
13
|
+
@listener.start do |e|
|
14
|
+
next until e.window
|
15
|
+
|
16
|
+
w = e.window
|
17
|
+
puts <<__OUTPUT__ if e.type == :active_window
|
18
|
+
######### change active window #########
|
19
|
+
id: #{w.id}
|
20
|
+
title: #{w.title}
|
21
|
+
name: #{w.app_name}
|
22
|
+
class: #{w.app_class}
|
23
|
+
pid: #{w.pid}
|
24
|
+
command: #{w.command}
|
25
|
+
__OUTPUT__
|
26
|
+
puts <<__OUTPUT__ if e.type == :title
|
27
|
+
######### change title #########
|
28
|
+
title: #{w.title}
|
29
|
+
__OUTPUT__
|
30
|
+
end
|
data/sample/simple.rb
ADDED
data/spec/atom_spec.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
require 'active_window_x'
|
4
|
+
|
5
|
+
include ActiveWindowX
|
6
|
+
|
7
|
+
describe Atom do
|
8
|
+
before do
|
9
|
+
@raw_display = mock Xlib::Display
|
10
|
+
@display = mock Display
|
11
|
+
@display.stub(:raw){@raw_display}
|
12
|
+
@display.stub(:kind_of?).with(Display).and_return(true)
|
13
|
+
@id = 123
|
14
|
+
@atom = Atom.new @display, @id
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#name' do
|
18
|
+
before do
|
19
|
+
@name = 'FOOO'
|
20
|
+
@display.should_receive(:atom_name).twice.with(@id).and_return(@name)
|
21
|
+
end
|
22
|
+
it 'should return a String as an atom name' do
|
23
|
+
@atom.name.should == @name
|
24
|
+
@atom.name.should == @name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# -*- coding:utf-8; mode:ruby; -*-
|
2
|
+
|
3
|
+
require 'active_window_x'
|
4
|
+
|
5
|
+
include ActiveWindowX
|
6
|
+
|
7
|
+
describe Display do
|
8
|
+
before do
|
9
|
+
@display_raw = mock Xlib::Display
|
10
|
+
Xlib.stub(:x_open_display).and_return(@display_raw)
|
11
|
+
@display = Display.new nil
|
12
|
+
@root_id = 9999
|
13
|
+
Xlib.stub(:default_root_window).with(@display_raw).and_return(@root_id)
|
14
|
+
Xlib.stub(:x_select_input).and_return(1)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#root_window' do
|
18
|
+
it 'should return the root window' do
|
19
|
+
r = @display.root_window
|
20
|
+
r.id.should == @root_id
|
21
|
+
r.should be_a RootWindow
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#next_event' do
|
26
|
+
before do
|
27
|
+
@root = @display.root_window
|
28
|
+
@root.select_input Xlib::PropertyChangeMask
|
29
|
+
@event = mock Xlib::XPropertyEvent
|
30
|
+
@event.stub(:type){@type}
|
31
|
+
@event.stub(:serial){1000}
|
32
|
+
@event.stub(:send_event){0}
|
33
|
+
@event.stub(:time){2000}
|
34
|
+
@event.stub(:state){nil}
|
35
|
+
@window_id = 222
|
36
|
+
@event.stub(:window){@window_id}
|
37
|
+
@atom_id = 333
|
38
|
+
@event.stub(:atom){@atom_id}
|
39
|
+
Xlib.should_receive(:x_next_event).and_return(@event)
|
40
|
+
end
|
41
|
+
context 'with PropertyChangeMask' do
|
42
|
+
before do
|
43
|
+
@type = Xlib::PropertyNotify
|
44
|
+
end
|
45
|
+
it 'should return a PropertyEvent' do
|
46
|
+
ev = @display.next_event
|
47
|
+
ev.type.should == @event.type
|
48
|
+
ev.window.id.should == @event.window
|
49
|
+
ev.atom.id.should == @event.atom
|
50
|
+
end
|
51
|
+
end
|
52
|
+
context 'with other event type' do
|
53
|
+
before do
|
54
|
+
@type = 0
|
55
|
+
end
|
56
|
+
it 'should return a PropertyEvent' do
|
57
|
+
ev = @display.next_event
|
58
|
+
ev.type.should == @event.type
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|