Annotation of previous/python-ui/tests/pygtk-hatari-embed-test.py, revision 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.