firewatir 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MozillaBaseElement.rb +1775 -1778
- data/container.rb +16 -5
- data/firewatir.rb +122 -57
- data/firewatir/winClicker.rb +122 -0
- data/firewatir/x11.rb +192 -0
- data/htmlelements.rb +2281 -2277
- data/unittests/html/textfields1.html +1 -1
- data/unittests/mozilla_all_tests.rb +2 -1
- data/unittests/setup.rb +1 -1
- metadata +43 -34
data/container.rb
CHANGED
@@ -816,7 +816,11 @@ module Container
|
|
816
816
|
# Test the index access.
|
817
817
|
# puts doc[35].to_s
|
818
818
|
end
|
819
|
-
|
819
|
+
|
820
|
+
def jssh_socket
|
821
|
+
$jssh_socket || @container.jssh_socket
|
822
|
+
end
|
823
|
+
|
820
824
|
#
|
821
825
|
# Description:
|
822
826
|
# Reads the javascript execution result from the jssh socket.
|
@@ -827,9 +831,10 @@ module Container
|
|
827
831
|
# Output:
|
828
832
|
# The javascript execution result as string.
|
829
833
|
#
|
830
|
-
def read_socket(socket =
|
834
|
+
def read_socket(socket = jssh_socket)
|
831
835
|
return_value = ""
|
832
836
|
data = ""
|
837
|
+
receive = true
|
833
838
|
#puts Thread.list
|
834
839
|
s = nil
|
835
840
|
while(s == nil) do
|
@@ -839,16 +844,22 @@ module Container
|
|
839
844
|
for stream in s[0]
|
840
845
|
data = stream.recv(1024)
|
841
846
|
#puts "data is : #{data}"
|
842
|
-
while(
|
847
|
+
while(receive)
|
848
|
+
#while(data.length == 1024)
|
843
849
|
return_value += data
|
844
|
-
|
850
|
+
if(return_value.include?("\n> "))
|
851
|
+
receive = false
|
852
|
+
else
|
853
|
+
data = stream.recv(1024)
|
854
|
+
end
|
855
|
+
#puts "return_value is : #{return_value}"
|
845
856
|
#puts "data length is : #{data.length}"
|
846
857
|
end
|
847
858
|
end
|
848
859
|
|
849
860
|
# If received data is less than 1024 characters or for last data
|
850
861
|
# we read in the above loop
|
851
|
-
return_value += data
|
862
|
+
#return_value += data
|
852
863
|
|
853
864
|
# Get the command prompt inserted by JSSH
|
854
865
|
#s = Kernel.select([socket] , nil , nil, 0.3)
|
data/firewatir.rb
CHANGED
@@ -255,6 +255,16 @@ module FireWatir
|
|
255
255
|
def get_window_number()
|
256
256
|
$jssh_socket.send("getWindows().length;\n", 0)
|
257
257
|
@@current_window = read_socket().to_i - 1
|
258
|
+
|
259
|
+
# Derek Berner 5/16/08
|
260
|
+
# If at any time a non-browser window like the "Downloads" window
|
261
|
+
# pops up, it will become the topmost window, so make sure we
|
262
|
+
# ignore it.
|
263
|
+
@@current_window = js_eval("getWindows().length").to_i - 1
|
264
|
+
while js_eval("getWindows()[#{@@current_window}].getBrowser") == ''
|
265
|
+
@@current_window -= 1;
|
266
|
+
end
|
267
|
+
|
258
268
|
# This will store the information about the window.
|
259
269
|
#@@window_stack.push(@@current_window)
|
260
270
|
#puts "here in get_window_number window number is #{@@current_window}"
|
@@ -329,6 +339,7 @@ module FireWatir
|
|
329
339
|
retry if no_of_tries < 3
|
330
340
|
raise UnableToStartJSShException, "Unable to connect to machine : #{MACHINE_IP} on port 9997. Make sure that JSSh is properly installed and Firefox is running with '-jssh' option"
|
331
341
|
end
|
342
|
+
@error_checkers = []
|
332
343
|
end
|
333
344
|
private :set_defaults
|
334
345
|
|
@@ -368,7 +379,9 @@ module FireWatir
|
|
368
379
|
#
|
369
380
|
def close()
|
370
381
|
#puts "current window number is : #{@@current_window}"
|
371
|
-
|
382
|
+
# Derek Berner 5/16/08
|
383
|
+
# Try to join thread only if there is exactly one open window
|
384
|
+
if js_eval("getWindows().length").to_i == 1
|
372
385
|
$jssh_socket.send(" getWindows()[0].close(); \n", 0)
|
373
386
|
@t.join if @t != nil
|
374
387
|
#sleep 5
|
@@ -576,70 +589,107 @@ module FireWatir
|
|
576
589
|
# Description:
|
577
590
|
# Waits for the page to get loaded.
|
578
591
|
#
|
579
|
-
def wait()
|
592
|
+
def wait(last_url = nil)
|
580
593
|
#puts "In wait function "
|
581
594
|
isLoadingDocument = ""
|
595
|
+
start = Time.now
|
596
|
+
|
582
597
|
while isLoadingDocument != "false"
|
583
|
-
|
584
|
-
isLoadingDocument = read_socket()
|
598
|
+
isLoadingDocument = js_eval("#{BROWSER_VAR}=#{WINDOW_VAR}.getBrowser(); #{BROWSER_VAR}.webProgress.isLoadingDocument;")
|
585
599
|
#puts "Is browser still loading page: #{isLoadingDocument}"
|
600
|
+
|
601
|
+
# Derek Berner 5/16/08
|
602
|
+
# Raise an exception if the page fails to load
|
603
|
+
if (Time.now - start) > 300
|
604
|
+
raise "Page Load Timeout"
|
605
|
+
end
|
586
606
|
end
|
587
|
-
|
588
|
-
#
|
589
|
-
#
|
590
|
-
#
|
591
|
-
#
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
607
|
+
# Derek Berner 5/16/08
|
608
|
+
# If the redirect is to a download attachment that does not reload this page, this
|
609
|
+
# method will loop forever. Therefore, we need to ensure that if this method is called
|
610
|
+
# twice with the same URL, we simply accept that we're done.
|
611
|
+
$jssh_socket.send("#{BROWSER_VAR}.contentDocument.URL;\n", 0)
|
612
|
+
url = read_socket()
|
613
|
+
|
614
|
+
if(url != last_url)
|
615
|
+
# Check for Javascript redirect. As we are connected to Firefox via JSSh. JSSh
|
616
|
+
# doesn't detect any javascript redirects so check it here.
|
617
|
+
# If page redirects to itself that this code will enter in infinite loop.
|
618
|
+
# So we currently don't wait for such a page.
|
619
|
+
# wait variable in JSSh tells if we should wait more for the page to get loaded
|
620
|
+
# or continue. -1 means page is not redirected. Anyother positive values means wait.
|
621
|
+
jssh_command = "var wait = -1; var meta = null; meta = #{BROWSER_VAR}.contentDocument.getElementsByTagName('meta');
|
622
|
+
if(meta != null)
|
599
623
|
{
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
wait_time =
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
624
|
+
var doc_url = #{BROWSER_VAR}.contentDocument.URL;
|
625
|
+
for(var i=0; i< meta.length;++i)
|
626
|
+
{
|
627
|
+
var content = meta[i].content;
|
628
|
+
var regex = new RegExp(\"^refresh$\", \"i\");
|
629
|
+
if(regex.test(meta[i].httpEquiv))
|
630
|
+
{
|
631
|
+
var arrContent = content.split(';');
|
632
|
+
var redirect_url = null;
|
633
|
+
if(arrContent.length > 0)
|
634
|
+
{
|
635
|
+
if(arrContent.length > 1)
|
636
|
+
redirect_url = arrContent[1];
|
637
|
+
|
638
|
+
if(redirect_url != null)
|
639
|
+
{
|
640
|
+
regex = new RegExp(\"^.*\" + redirect_url + \"$\");
|
641
|
+
if(!regex.test(doc_url))
|
642
|
+
{
|
643
|
+
wait = arrContent[0];
|
644
|
+
}
|
645
|
+
}
|
646
|
+
break;
|
647
|
+
}
|
648
|
+
}
|
649
|
+
}
|
650
|
+
}
|
651
|
+
wait;"
|
652
|
+
#puts "command in wait is : #{jssh_command}"
|
653
|
+
jssh_command = jssh_command.gsub(/\n/, "")
|
654
|
+
$jssh_socket.send("#{jssh_command}; \n", 0)
|
655
|
+
wait_time = read_socket();
|
656
|
+
#puts "wait time is : #{wait_time}"
|
657
|
+
begin
|
658
|
+
wait_time = wait_time.to_i
|
659
|
+
if(wait_time != -1)
|
660
|
+
sleep(wait_time)
|
661
|
+
# Call wait again. In case there are multiple redirects.
|
662
|
+
$jssh_socket.send("#{BROWSER_VAR} = #{WINDOW_VAR}.getBrowser(); \n",0)
|
663
|
+
read_socket()
|
664
|
+
wait(url)
|
665
|
+
end
|
666
|
+
rescue
|
667
|
+
end
|
668
|
+
end
|
641
669
|
set_browser_document()
|
670
|
+
run_error_checks()
|
671
|
+
return self
|
642
672
|
end
|
673
|
+
|
674
|
+
# Add an error checker that gets called on every page load.
|
675
|
+
#
|
676
|
+
# * checker - a Proc object
|
677
|
+
def add_checker(checker)
|
678
|
+
@error_checkers << checker
|
679
|
+
end
|
680
|
+
|
681
|
+
# Disable an error checker
|
682
|
+
#
|
683
|
+
# * checker - a Proc object that is to be disabled
|
684
|
+
def disable_checker(checker)
|
685
|
+
@error_checkers.delete(checker)
|
686
|
+
end
|
687
|
+
|
688
|
+
# Run the predefined error checks. This is automatically called on every page load.
|
689
|
+
def run_error_checks
|
690
|
+
@error_checkers.each { |e| e.call(self) }
|
691
|
+
end
|
692
|
+
|
643
693
|
|
644
694
|
#def jspopup_appeared(popupText = "", wait = 2)
|
645
695
|
# winHelper = WindowHelper.new()
|
@@ -1084,6 +1134,21 @@ module FireWatir
|
|
1084
1134
|
end
|
1085
1135
|
alias showFrames show_frames
|
1086
1136
|
|
1137
|
+
# 5/16/08 Derek Berner
|
1138
|
+
# Wrapper method to send JS commands concisely,
|
1139
|
+
# and propagate errors
|
1140
|
+
def js_eval(str)
|
1141
|
+
#puts "JS Eval: #{str}"
|
1142
|
+
$jssh_socket.send("#{str};\n",0)
|
1143
|
+
value = read_socket()
|
1144
|
+
if md=/^(\w+)Error:(.*)$/.match(value)
|
1145
|
+
eval "class JS#{md[1]}Error\nend"
|
1146
|
+
raise (eval "JS#{md[1]}Error"), md[2]
|
1147
|
+
end
|
1148
|
+
#puts "Value: #{value}"
|
1149
|
+
value
|
1150
|
+
end
|
1151
|
+
|
1087
1152
|
end # Class Firefox
|
1088
1153
|
|
1089
1154
|
#
|
@@ -0,0 +1,122 @@
|
|
1
|
+
if /linux/i.match(RUBY_PLATFORM)
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'x11'))
|
3
|
+
|
4
|
+
# Linux/X11 implementation of WinClicker.
|
5
|
+
# Not all functionality is present because of the differences between X11
|
6
|
+
# and Win32.
|
7
|
+
class WinClicker
|
8
|
+
|
9
|
+
def clickJavaScriptDialog(button="OK")
|
10
|
+
click_window_button(/The page/,button)
|
11
|
+
end
|
12
|
+
|
13
|
+
def clickJSDialog_Thread(button="OK")
|
14
|
+
puts "clickJSDialog_Thread Starting waiting..."
|
15
|
+
sleep 3
|
16
|
+
puts " clickJSDialog_Thread ... resuming"
|
17
|
+
n = 0
|
18
|
+
while n < 3
|
19
|
+
sleep 1
|
20
|
+
click_window_button(/The page/,button)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def clearSecurityAlertBox
|
25
|
+
click_window_button(/Unknown Authority/, "OK")
|
26
|
+
click_window_button(/Domain Name Mismatch/, "Cancel")
|
27
|
+
end
|
28
|
+
|
29
|
+
def clickWindowsButton(title, button, maxWaitTime=30)
|
30
|
+
start = Time.now
|
31
|
+
w = window_by_title(title)
|
32
|
+
until w || (Time.now - start > maxWaitTime)
|
33
|
+
sleep(2) # Window search is pretty CPU intensive, so relax the requirement
|
34
|
+
w = window_by_title(title)
|
35
|
+
end
|
36
|
+
unless w
|
37
|
+
puts "clickWindowsButton: Cant make window active in specified time ( " + maxWaitTime.to_s + ") - no handle"
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
click_button(w,button)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Since it's impossible to read the button text in X11 windows,
|
46
|
+
# we have to specify keystrokes for the button names given the title.
|
47
|
+
# TODO: A more elegant solution, or expand this list (to fill out popup text boxes for basic HTTP auth, perhaps).
|
48
|
+
@@window_keys = [
|
49
|
+
[/Unknown Authority/i, {'ok' => [:enter], 'cancel' => [:tab,:tab,:tab,:enter]}],
|
50
|
+
[/Domain Name Mismatch/i, {'ok' => [:tab, :enter], 'cancel' => [:enter]}],
|
51
|
+
[/Opening/i, {'ok' => [:sleep,:enter], 'cancel' => [:tab,:tab,:tab,:enter]}],
|
52
|
+
[/The page at .* says/i, {'ok' => [:enter], 'cancel' => [:tab,:enter]}]
|
53
|
+
]
|
54
|
+
|
55
|
+
# Collection of all current firefox windows
|
56
|
+
def firefox_windows(w = nil)
|
57
|
+
collection = []
|
58
|
+
windows = nil
|
59
|
+
if w
|
60
|
+
windows = [w]
|
61
|
+
else
|
62
|
+
windows = X11::Display.instance.screens.collect{|s| s.root_window}
|
63
|
+
end
|
64
|
+
windows.each do |window|
|
65
|
+
if window.class == 'Gecko'
|
66
|
+
collection << window
|
67
|
+
end
|
68
|
+
window.children.each do |c|
|
69
|
+
collection << firefox_windows(c)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
return collection.flatten.compact
|
73
|
+
end
|
74
|
+
|
75
|
+
def window_by_title(title,windows=nil)
|
76
|
+
pattern = nil
|
77
|
+
if title.is_a?(Regexp)
|
78
|
+
pattern = title
|
79
|
+
else
|
80
|
+
pattern = Regexp.compile(title,Regexp::IGNORECASE)
|
81
|
+
end
|
82
|
+
windows ||= X11::Display.instance.screens.collect{|s| s.root_window}
|
83
|
+
if window = windows.find{|w| w.class == 'Gecko' && pattern.match(w.name)}
|
84
|
+
return window
|
85
|
+
else
|
86
|
+
children = windows.reject{|w| w.class == 'Gecko'}.collect{|w| w.children}.flatten.compact
|
87
|
+
if children.length > 0
|
88
|
+
return window_by_title(pattern,children)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
return nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def keystrokes(window,button)
|
95
|
+
keys = @@window_keys.find{|wk| wk.first.match(window.name)}
|
96
|
+
if keys
|
97
|
+
return keys.last[button.downcase]
|
98
|
+
else
|
99
|
+
return false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def click_button(window, button)
|
104
|
+
keys = nil
|
105
|
+
if button.is_a?(Symbol)
|
106
|
+
keys = [button]
|
107
|
+
else
|
108
|
+
keys = keystrokes(window,button)
|
109
|
+
end
|
110
|
+
return unless keys
|
111
|
+
keys.each do |key|
|
112
|
+
if key == :sleep
|
113
|
+
@sleep_next = 1
|
114
|
+
next
|
115
|
+
end
|
116
|
+
window.send_key(key,@sleep_next)
|
117
|
+
@sleep_next = nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
data/firewatir/x11.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
require 'dl/import'
|
2
|
+
require 'dl/struct'
|
3
|
+
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
module X11
|
7
|
+
class << self
|
8
|
+
include X11 # Do this so we can call imported libraries directly on X11
|
9
|
+
end
|
10
|
+
|
11
|
+
# Load the X11 library
|
12
|
+
extend DL::Importable
|
13
|
+
dlload "libX11.so"
|
14
|
+
|
15
|
+
# Import necessary functions from X11 here.
|
16
|
+
import("XOpenDisplay", "unsigned long", ["char*"])
|
17
|
+
import("XScreenCount", "int", ["unsigned long"])
|
18
|
+
import("XRootWindow", "unsigned long", ["unsigned long","int"])
|
19
|
+
|
20
|
+
import("XFree", "int", ["void*"])
|
21
|
+
|
22
|
+
import("XFetchName", "int", ["unsigned long","unsigned long","char**"])
|
23
|
+
import("XGetClassHint", "int", ["unsigned long","unsigned long","void*"])
|
24
|
+
import("XQueryTree", "int", ["unsigned long","unsigned long","unsigned long*","unsigned long*","unsigned long**","unsigned int*"])
|
25
|
+
|
26
|
+
import("XSetInputFocus", "int", ["unsigned long","unsigned long","int","long"])
|
27
|
+
import("XSendEvent", "int", ["unsigned long","unsigned long","int","long","void*"])
|
28
|
+
import("XFlush", "int", ["unsigned long"])
|
29
|
+
|
30
|
+
# Structs we will use in API calls.
|
31
|
+
# Pointer structs are necessary when the API uses a pointer parameter for a return value.
|
32
|
+
ULPPointer = struct [
|
33
|
+
"long* value"
|
34
|
+
]
|
35
|
+
ULPointer = struct [
|
36
|
+
"long value"
|
37
|
+
]
|
38
|
+
CPPointer = struct [
|
39
|
+
"char* value"
|
40
|
+
]
|
41
|
+
UIPointer = struct [
|
42
|
+
"int value"
|
43
|
+
]
|
44
|
+
# Info about window class
|
45
|
+
XClassHint = struct [
|
46
|
+
"char* res_name",
|
47
|
+
"char* res_class"
|
48
|
+
]
|
49
|
+
# Event struct for key presses
|
50
|
+
XKeyEvent = struct [
|
51
|
+
"int type",
|
52
|
+
"long serial",
|
53
|
+
"int send_event",
|
54
|
+
"long display",
|
55
|
+
"long window",
|
56
|
+
"long root",
|
57
|
+
"long subwindow",
|
58
|
+
"long time",
|
59
|
+
"int x",
|
60
|
+
"int y",
|
61
|
+
"int x_root",
|
62
|
+
"int y_root",
|
63
|
+
"int state",
|
64
|
+
"int keycode",
|
65
|
+
"int same_screen"
|
66
|
+
]
|
67
|
+
|
68
|
+
# End of library imports.
|
69
|
+
|
70
|
+
# X11 Display. Singleton -- assumes single display.
|
71
|
+
# Assumes the current display is the same as the one running FireFox.
|
72
|
+
# Represented by memory pointer (which we treat in-code as an unsigned long).
|
73
|
+
class Display
|
74
|
+
include Singleton
|
75
|
+
|
76
|
+
def initialize
|
77
|
+
@xdisplay = X11.xOpenDisplay("");
|
78
|
+
end
|
79
|
+
|
80
|
+
# Array of screens associated with this display.
|
81
|
+
def screens
|
82
|
+
nScreens = X11.xScreenCount(@xdisplay);
|
83
|
+
(0...nScreens).collect{|n| Screen.new(n,@xdisplay)}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# A display screen, for multi-monitor displays like mine ;-)
|
88
|
+
# Represented by display pointer and screen number.
|
89
|
+
class Screen
|
90
|
+
def initialize(screen_num,xdisplay)
|
91
|
+
@screen_num = screen_num
|
92
|
+
@xdisplay = xdisplay
|
93
|
+
end
|
94
|
+
|
95
|
+
# Root window containing all other windows in this screen.
|
96
|
+
def root_window
|
97
|
+
Window.new(X11.xRootWindow(@xdisplay,@screen_num),@screen_num,@xdisplay)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# An X11 Window (toplevel window, widget, applet, etc.)
|
102
|
+
# Represented by its XID, an unsigned long.
|
103
|
+
class Window
|
104
|
+
attr_reader :xid, :name, :class, :hint, :parent
|
105
|
+
|
106
|
+
def initialize(xid,screen_num,xdisplay,parent=nil)
|
107
|
+
@xid = xid
|
108
|
+
@screen_num = screen_num
|
109
|
+
@xdisplay = xdisplay
|
110
|
+
@parent = parent
|
111
|
+
load_standard
|
112
|
+
end
|
113
|
+
|
114
|
+
# Child windows
|
115
|
+
def children
|
116
|
+
tree[:children].collect{|c| Window.new(c,@screen_num,@xdisplay,self)}
|
117
|
+
end
|
118
|
+
|
119
|
+
# XID of parent window
|
120
|
+
def parent_xid
|
121
|
+
parent ? parent.xid : nil
|
122
|
+
end
|
123
|
+
|
124
|
+
# Send a key press to this window
|
125
|
+
def send_key(key=:enter,sleep=nil)
|
126
|
+
# TODO expand this list out, add support for shift, etc.
|
127
|
+
@@keys = {:enter => 36, :tab => 23} unless defined?@@keys
|
128
|
+
keycode = @@keys[key]
|
129
|
+
X11.xSetInputFocus(@xdisplay, @xid, 1, 0)
|
130
|
+
sleep(sleep) if sleep
|
131
|
+
e = create_key_event
|
132
|
+
e.keycode = keycode
|
133
|
+
e.type = 2 # press
|
134
|
+
X11.xSendEvent(@xdisplay,@xid,1,1,e)
|
135
|
+
e.type = 3 # release
|
136
|
+
X11.xSendEvent(@xdisplay,@xid,1,2,e)
|
137
|
+
X11.xFlush(@xdisplay)
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
# Retrieve this window's portion of the window tree
|
143
|
+
# Includes display root, parent, and children
|
144
|
+
def tree
|
145
|
+
tree = {:children => [], :parent => 0, :root => 0}
|
146
|
+
children = ULPPointer.malloc
|
147
|
+
root = ULPointer.malloc
|
148
|
+
parent = ULPointer.malloc
|
149
|
+
n = UIPointer.malloc
|
150
|
+
r=X11.xQueryTree(@xdisplay,@xid,root,parent,children,n)
|
151
|
+
tree[:parent] = parent.value
|
152
|
+
tree[:root] = root.value
|
153
|
+
tree[:children] = children.value.to_s(4*n.value).unpack("L*") if children.value
|
154
|
+
tree
|
155
|
+
end
|
156
|
+
|
157
|
+
# Load some standard attributes (name and class)
|
158
|
+
def load_standard
|
159
|
+
name = CPPointer.malloc
|
160
|
+
if X11.xFetchName(@xdisplay,@xid,name) != 0
|
161
|
+
@name = name.value.to_s
|
162
|
+
X11.xFree name.value
|
163
|
+
end
|
164
|
+
classHint = XClassHint.malloc
|
165
|
+
res = X11.xGetClassHint(@xdisplay,@xid,classHint)
|
166
|
+
if res != 0 then
|
167
|
+
@class = classHint.res_name.to_s
|
168
|
+
@hint = classHint.res_class.to_s
|
169
|
+
X11.xFree classHint.res_name
|
170
|
+
X11.xFree classHint.res_class
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Create an X11 Key Event for this window and set defaults
|
175
|
+
def create_key_event
|
176
|
+
ke = XKeyEvent.malloc
|
177
|
+
ke.serial = 0
|
178
|
+
ke.send_event = 1
|
179
|
+
ke.display = @xdisplay
|
180
|
+
ke.window = @xid
|
181
|
+
ke.subwindow = 0
|
182
|
+
ke.root = tree[:root]
|
183
|
+
ke.time = Time.now.sec
|
184
|
+
ke.state = 0
|
185
|
+
ke.same_screen = 0
|
186
|
+
return ke
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|