|
|
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.