|
|
1.1 root 1: #!/usr/bin/python
2: #
3: # Tests embedding hatari with three different methods:
4: # "hatari": ask Hatari to reparent to given window
5: # "sdl": Give SDL window into which it should reparent
6: # -> SDL doesn't handle (mouse, key, expose) events
7: # although according to "xev" it's window receives them!
8: # Bug in SDL (not one of the originally needed features?)?
9: # "reparent": Find Hatari window and reparent it into pygtk widget in python
10: # - Needs "xwininfo" and "awk"
11: #
12: # Using three alternative widgets:
13: # "drawingarea"
14: # "eventbox"
15: # "socket"
16: #
17: # Results:
18: # reparent+eventbox
19: # -> PyGtk reparents Hatari under something on rootwindow instead
20: # (reparening eventbox under Hatari window works fine though...)
21: # reparent+socket
22: # -> Hatari seems to be reparented back to where it was
23: # sdl+anything
24: # -> all events are lost
25: # hatari+socket
26: # -> seems to work fine
27: import os
28: import sys
29: import gtk
30: import time
31: import gobject
32:
33: def usage(error):
34: print "\nusage: %s <widget> <embed method>\n" % sys.argv[0].split(os.path.sep)[-1]
35: print "Opens window with given <widget>, runs Hatari and tries to embed it"
36: print "with given <method>\n"
37: print "<widget> can be <drawingarea|eventbox|socket>"
38: print "<method> can be <sdl|hatari|reparent>\n"
39: print "ERROR: %s\n" % error
40: sys.exit(1)
41:
42:
43: class AppUI():
44: hatari_wd = 640
45: hatari_ht = 400
46:
47: def __init__(self, widget, method):
48: if method in ("hatari", "reparent", "sdl"):
49: self.method = method
50: else:
51: usage("unknown <method> '%s'" % method)
52: if widget == "drawingarea":
53: widgettype = gtk.DrawingArea
54: elif widget == "eventbox":
55: widgettype = gtk.EventBox
56: elif widget == "socket":
57: # XEMBED socket for Hatari/SDL
58: widgettype = gtk.Socket
59: else:
60: usage("unknown <widget> '%s'" % widget)
61: self.window = self.create_window()
62: self.add_hatari_parent(self.window, widgettype)
63: gobject.timeout_add(1*1000, self.timeout_cb)
64:
65: def create_window(self):
66: window = gtk.Window(gtk.WINDOW_TOPLEVEL)
67: window.connect("destroy", self.do_quit)
68: return window
69:
70: def do_quit(self, widget):
71: if self.hatari_pid:
72: os.kill(self.hatari_pid, 9)
73: print "killed Hatari PID %d" % self.hatari_pid
74: self.hatari_pid = 0
75: gtk.main_quit()
76:
77: def add_hatari_parent(self, parent, widgettype):
78: # Note: CAN_FOCUS has to be set for the widget embedding Hatari
79: # and *unset* for everything else, otherwise Hatari doesn't
80: # receive *any* keyevents.
81: self.hatari_pid = 0
82: vbox = gtk.VBox()
83: button = gtk.Button("Test Button")
84: button.unset_flags(gtk.CAN_FOCUS)
85: vbox.add(button)
86: widget = widgettype()
87: widget.set_size_request(self.hatari_wd, self.hatari_ht)
88: widget.set_events(gtk.gdk.ALL_EVENTS_MASK)
89: widget.set_flags(gtk.CAN_FOCUS)
90: self.hatariparent = widget
91: # TODO: when running 320x200, parent could be centered to here
92: vbox.add(widget)
93: # test focus
94: label = gtk.Label("Test SpinButton:")
95: vbox.add(label)
96: spin = gtk.SpinButton()
97: spin.set_range(0, 10)
98: spin.set_digits(0)
99: spin.set_numeric(True)
100: spin.set_increments(1, 2)
101: # otherwise Hatari doesn't receive keys!!!
102: spin.unset_flags(gtk.CAN_FOCUS)
103: vbox.add(spin)
104: parent.add(vbox)
105:
106: def timeout_cb(self):
107: self.do_hatari_method()
108: return False # only once
109:
110: def do_hatari_method(self):
111: pid = os.fork()
112: if pid < 0:
113: print "ERROR: fork()ing Hatari failed!"
114: return
115: if pid:
116: # in parent
117: if self.method == "reparent":
118: hatari_win = self.find_hatari_window()
119: if hatari_win:
120: self.reparent_hatari_window(hatari_win)
121: self.hatari_pid = pid
122: else:
123: os.kill(pid, signal.SIGKILL)
124: print "killed process with PID %d" % pid
125: self.hatari_pid = 0
126: else:
127: # method == "sdl" or "hatari"
128: self.hatari_pid = pid
129: else:
130: # child runs Hatari
131: args = ("hatari", "-m", "-z", "2")
132: os.execvpe("hatari", args, self.get_hatari_env())
133:
134: def get_hatari_env(self):
135: if self.method == "reparent":
136: return os.environ
137: # tell SDL to use (embed itself inside) given widget's window
138: win_id = self.hatariparent.window.xid
139: env = os.environ
140: if self.method == "sdl":
141: env["SDL_WINDOWID"] = str(win_id)
142: elif self.method == "hatari":
143: env["HATARI_PARENT_WIN"] = str(win_id)
144: return env
145:
146: def find_hatari_window(self):
147: # find hatari window by its WM class string and reparent it
148: cmd = """xwininfo -root -tree|awk '/"hatari" "hatari"/{print $1}'"""
149: counter = 0
150: while counter < 8:
151: pipe = os.popen(cmd)
152: windows = []
153: for line in pipe.readlines():
154: windows.append(int(line, 16))
155: try:
156: pipe.close()
157: except IOError:
158: # handle child process exiting silently
159: pass
160: if not windows:
161: counter += 1
162: print "WARNING: no Hatari window found yet, retrying..."
163: time.sleep(1)
164: continue
165: if len(windows) > 1:
166: print "WARNING: multiple Hatari windows, picking first one..."
167: return windows[0]
168: print "ERROR: no windows with the 'hatari' WM class found"
169: return None
170:
171: def reparent_hatari_window(self, hatari_win):
172: window = gtk.gdk.window_foreign_new(hatari_win)
173: if not window:
174: print "ERROR: Hatari window (ID: 0x%x) reparenting failed!" % hatari_win
175: return False
176: if not self.hatariparent.window:
177: print "ERROR: where hatariparent disappeared?"
178: return False
179: print "Found Hatari window ID: 0x%x, reparenting..." % hatari_win
180: print "...to container window ID: 0x%x" % self.hatariparent.window.xid
181: window.reparent(self.hatariparent.window, 0, 0)
182: #window.reparent(self.hatariparent.get_toplevel().window, 0, 0)
183: #window.reparent(self.hatariparent.get_root_window(), 0, 0)
184: #window.show()
185: #window.raise_()
186: # If python would destroy the Gtk widget when it goes out of scope,
187: # the foreign window widget destructor would delete Hatari window.
188: # So, keep a reference
189: #self.hatariwindow = window
190: return True
191:
192: def run(self):
193: self.window.show_all()
194: gtk.main()
195:
196:
197: if len(sys.argv) != 3:
198: usage("wrong number of arguments")
199: app = AppUI(sys.argv[1], sys.argv[2])
200: app.run()
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.