Annotation of hatari/python-ui/tests/pygtk-hatari-embed-test.py, revision 1.1.1.1

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()

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.