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