From 12c08f6aa55c75d9b5ef71bb636d4043a3981f9e Mon Sep 17 00:00:00 2001 From: Eratosthenes Date: Tue, 2 Mar 2010 23:12:55 -0500 Subject: [PATCH 01/33] Delete xwininfo stuff. Reinstall Xlib stuff. --- pyfpdb/Tables_Demo.py | 6 +-- pyfpdb/XTables.py | 100 ++++++++++++++---------------------------- 2 files changed, 34 insertions(+), 72 deletions(-) diff --git a/pyfpdb/Tables_Demo.py b/pyfpdb/Tables_Demo.py index 92bb1a3c..199e81c2 100755 --- a/pyfpdb/Tables_Demo.py +++ b/pyfpdb/Tables_Demo.py @@ -3,7 +3,7 @@ Main program module to test/demo the Tables subclasses. """ -# Copyright 2008 - 2009, Ray E. Barker +# Copyright 2008 - 2010, Ray E. Barker # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,10 +24,8 @@ Main program module to test/demo the Tables subclasses. # Standard Library modules import sys import os -import re # pyGTK modules -import pygtk import gtk import gobject @@ -103,7 +101,7 @@ if __name__=="__main__": fake = fake_hud(table) print "fake =", fake -# gobject.timeout_add(100, check_on_table, table, fake) + gobject.timeout_add(100, check_on_table, table, fake) print "calling main" gtk.main() diff --git a/pyfpdb/XTables.py b/pyfpdb/XTables.py index 29a9b1d9..6b06eb0f 100644 --- a/pyfpdb/XTables.py +++ b/pyfpdb/XTables.py @@ -1,11 +1,9 @@ #!/usr/bin/env python -"""Discover_Tables.py +"""XTables.py -Inspects the currently open windows and finds those of interest to us--that is -poker table windows from supported sites. Returns a list -of Table_Window objects representing the windows found. +XWindows specific methods for TableWindows Class. """ -# Copyright 2008 - 2009, Ray E. Barker +# Copyright 2008 - 2010, Ray E. Barker # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -25,14 +23,11 @@ of Table_Window objects representing the windows found. # Standard Library modules import re -import os # pyGTK modules -import pygtk import gtk # Other Library modules -import Xlib import Xlib.display # FreePokerTools modules @@ -46,72 +41,41 @@ name_atom = disp.get_atom("WM_NAME", 1) class Table(Table_Window): def find_table_parameters(self, search_string): -# self.window = None -# done_looping = False -# for outside in root.query_tree().children: -# for inside in outside.query_tree().children: -# if done_looping: break -# prop = inside.get_property(name_atom, Xlib.Xatom.STRING, 0, 1000) -# print prop -# if prop is None: continue -# if prop.value and re.search(search_string, prop.value): -# if self.check_bad_words(prop.value): continue -## if inside.get_wm_name() and re.search(search_string, inside.get_wm_name()): -## if self.check_bad_words(inside.get_wm_name()): -## print "bad word =", inside.get_wm_name() -## continue -# self.window = inside -# self.parent = outside -# done_looping = True -# break + self.window = None + done_looping = False + for outside in root.query_tree().children: + for inside in outside.query_tree().children: + if done_looping: break + if inside.get_wm_name() and re.search(search_string, inside.get_wm_name()): + if self.check_bad_words(inside.get_wm_name()): continue + self.window = inside + self.parent = outside + done_looping = True + break - window_number = None - for listing in os.popen('xwininfo -root -tree').readlines(): - if re.search(search_string, listing): -# print listing - mo = re.match('\s+([\dxabcdef]+) (.+):\s\(\"([a-zA-Z.]+)\".+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', listing) - self.number = int( mo.group(1), 0) - self.width = int( mo.group(4) ) - self.height = int( mo.group(5) ) - self.x = int( mo.group(6) ) - self.y = int( mo.group(7) ) - self.title = re.sub('\"', '', mo.group(2)) - self.exe = "" # not used? - self.hud = None -# done_looping = False -# for outside in root.query_tree().children: -# for inside in outside.query_tree().children: -# if done_looping: break -# if inside.id == window_number: -# self.window = inside -# self.parent = outside -# done_looping = True -# break - - if window_number is None: + if self.window == None or self.parent == None: + print "Window %s not found. Skipping." % search_string return None -# my_geo = self.window.get_geometry() -# pa_geo = self.parent.get_geometry() -# -# self.x = pa_geo.x + my_geo.x -# self.y = pa_geo.y + my_geo.y -# self.width = my_geo.width -# self.height = my_geo.height -# self.exe = self.window.get_wm_class()[0] -# self.title = self.window.get_wm_name() -# self.site = "" -# self.hud = None + my_geo = self.window.get_geometry() + pa_geo = self.parent.get_geometry() -# window_string = str(self.window) - mo = re.match('Xlib\.display\.Window\(([\dxabcdef]+)', window_string) + self.x = pa_geo.x + my_geo.x + self.y = pa_geo.y + my_geo.y + self.width = my_geo.width + self.height = my_geo.height + self.exe = self.window.get_wm_class()[0] + self.title = self.window.get_wm_name() + self.site = "" + self.hud = None + self.number = self.get_xid() + + def get_xid(self): + mo = re.match('Xlib\.display\.Window\(([\dxabcdef]+)', str(self.window)) if not mo: - print "Not matched" - self.gdk_handle = None + return None else: - self.number = int( mo.group(1), 0) - print "number =", self.number -# self.gdk_handle = gtk.gdk.window_foreign_new(int(self.number)) + return int( mo.group(1), 0) def get_geometry(self): try: From cc4f8e2beaf42753036101d9b41e4dfbfd688dc5 Mon Sep 17 00:00:00 2001 From: Eratosthenes Date: Mon, 30 Aug 2010 11:55:34 -0400 Subject: [PATCH 02/33] added some intermediate output. --- pyfpdb/XTables.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pyfpdb/XTables.py b/pyfpdb/XTables.py index 6b06eb0f..03b18073 100644 --- a/pyfpdb/XTables.py +++ b/pyfpdb/XTables.py @@ -29,6 +29,7 @@ import gtk # Other Library modules import Xlib.display +from Xlib import Xatom # FreePokerTools modules from TableWindow import Table_Window @@ -37,6 +38,7 @@ from TableWindow import Table_Window disp = Xlib.display.Display() root = disp.screen().root name_atom = disp.get_atom("WM_NAME", 1) +icon_atom = disp.get_atom("WM_ICON_NAME", 1) class Table(Table_Window): @@ -46,7 +48,12 @@ class Table(Table_Window): for outside in root.query_tree().children: for inside in outside.query_tree().children: if done_looping: break - if inside.get_wm_name() and re.search(search_string, inside.get_wm_name()): +# if inside.get_wm_name() and re.search(search_string, inside.get_wm_name()): + if inside.get_wm_name() and re.search(search_string, inside.get_wm_icon_name()): + print "inside = ", inside + print "\n".join(dir(inside)) + for atom in inside.list_properties(): print atom, disp.get_atom_name(atom), inside.get_full_property(atom, Xatom.STRING).value + if hasattr(inside, "window"): print "window = ", str(inside.window) if self.check_bad_words(inside.get_wm_name()): continue self.window = inside self.parent = outside From 9772129f30a14a94bf806f9ea288ec817f5952a5 Mon Sep 17 00:00:00 2001 From: Eratosthenes Date: Tue, 7 Sep 2010 09:50:29 -0400 Subject: [PATCH 03/33] Rewrite of XTables to use Xlib. Considerable cleanup in TableWindow. --- pyfpdb/HandHistoryConverter.py | 13 ++- pyfpdb/TableWindow.py | 179 +++++++++++++++++++++++++-------- pyfpdb/Tables_Demo.py | 32 +++--- pyfpdb/XTables.py | 96 +++++++++--------- 4 files changed, 212 insertions(+), 108 deletions(-) diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 2053eba9..4dfc3a80 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -501,12 +501,23 @@ or None if we fail to get the info """ else: return table_name - + @staticmethod + def getTableNoRe(tournament): + "Returns string to search window title for tournament table no." +# Full Tilt: $30 + $3 Tournament (181398949), Table 1 - 600/1200 Ante 100 - Limit Razz +# PokerStars: WCOOP 2nd Chance 02: $1,050 NLHE - Tournament 307521826 Table 1 - Blinds $30/$60 + return "%s.+Table (\d+)" % (tournament, ) def getTableTitleRe(config, sitename, *args, **kwargs): "Returns string to search in windows titles for current site" return getSiteHhc(config, sitename).getTableTitleRe(*args, **kwargs) +def getTableNoRe(config, sitename, *args, **kwargs): + "Returns string to search window titles for tournament table no." + return getSiteHhc(config, sitename).getTableNoRe(*args, **kwargs) + + + def getSiteHhc(config, sitename): "Returns HHC class for current site" hhcName = config.supported_sites[sitename].converter diff --git a/pyfpdb/TableWindow.py b/pyfpdb/TableWindow.py index aa994bfe..a77a9aaa 100644 --- a/pyfpdb/TableWindow.py +++ b/pyfpdb/TableWindow.py @@ -1,11 +1,13 @@ #!/usr/bin/env python -"""Discover_TableWindow.py +"""Base class for interacting with poker client windows. -Inspects the currently open windows and finds those of interest to us--that is -poker table windows from supported sites. Returns a list -of Table_Window objects representing the windows found. +There are currently subclasses for X and Windows. + +The class queries the poker client window for data of interest, such as +size and location. It also controls the signals to alert the HUD when the +client has been resized, destroyed, etc. """ -# Copyright 2008 - 2009, Ray E. Barker +# Copyright 2008 - 2010, Ray E. Barker # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,35 +26,36 @@ of Table_Window objects representing the windows found. ######################################################################## # Standard Library modules -import os -import sys +import re # pyGTK modules -import pygtk import gtk import gobject # FreePokerTools modules -import Configuration -#if os.name == "posix": -# import XTables -#elif os.name == "nt": -# import WinTables +from HandHistoryConverter import getTableTitleRe +from HandHistoryConverter import getTableNoRe -# Global used for figuring out the current game being played from the title -# The dict key is the fpdb name for the game +# Global used for figuring out the current game being played from the title. +# The dict key is a tuple of (limit type, category) for the game. # The list is the names for those games used by the supported poker sites -# This is currently only used for HORSE, so it only needs to support those +# This is currently only used for mixed games, so it only needs to support those # games on PokerStars and Full Tilt. -game_names = { #fpdb name Stars Name FTP Name - "holdem" : ("Hold\'em" , ), - "omahahilo" : ("Omaha H/L" , ), - "studhilo" : ("Stud H/L" , ), - "razz" : ("Razz" , ), - "studhi" : ("Stud" , "Stud Hi") +nlpl_game_names = { #fpdb name Stars Name FTP Name (if different) + ("nl", "holdem" ) : ("No Limit Hold\'em" , ), + ("pl", "holdem" ) : ("Pot Limit Hold\'em" , ), + ("pl", "omahahi" ) : ("Pot Limit Omaha" ,"Pot Limit Omaha Hi" ), + } +limit_game_names = { #fpdb name Stars Name FTP Name + ("fl", "holdem" ) : ("Limit Hold\'em" , ), + ("fl", "omahahilo" ) : ("Limit Omaha H/L" , ), + ("fl", "studhilo" ) : ("Limit Stud H/L" , ), + ("fl", "razz" ) : ("Limit Razz" , ), + ("fl", "studhi" ) : ("Limit Stud" , "Stud Hi"), + ("fl", "27_3draw" ) : ("Limit Triple Draw 2-7 Lowball", ) } -# A window title might have our table name + one of theses words/ +# A window title might have our table name + one of these words/ # phrases. If it has this word in the title, it is not a table. bad_words = ('History for table:', 'HUD:', 'Chat:') @@ -75,11 +78,19 @@ gobject.signal_new("client_destroyed", gtk.Window, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) +gobject.signal_new("game_changed", gtk.Window, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)) + +gobject.signal_new("table_changed", gtk.Window, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)) + # Each TableWindow object must have the following attributes correctly populated: # tw.name = the table name from the title bar, which must to match the table name -# from the corresponding hand history. -# tw.site = the site name, e.g. PokerStars, FullTilt. This must match the site -# name specified in the config file. +# from the corresponding hand record in the db. # tw.number = This is the system id number for the client table window in the # format that the system presents it. This is Xid in Xwindows and # hwnd in Microsoft Windows. @@ -91,61 +102,143 @@ gobject.signal_new("client_destroyed", gtk.Window, # to the top left of the display screen. This also does not include the # title bar and window borders. To put it another way, this is the # screen location of (0, 0) in the working window. +# tournament = Tournament number for a tournament or None for a cash game. +# table = Table number for a tournament. +# gdkhandle = +# window = +# parent = +# game = +# search_string = class Table_Window(object): - def __init__(self, search_string, table_name = None, tournament = None, table_number = None): + def __init__(self, config, site, table_name = None, tournament = None, table_number = None): + self.config = config + self.site = site if tournament is not None and table_number is not None: - print "tournament %s, table %s" % (tournament, table_number) self.tournament = int(tournament) self.table = int(table_number) self.name = "%s - %s" % (self.tournament, self.table) + self.type = "tour" + table_kwargs = dict(tournament = self.tournament, table_number = self.table) + self.tableno_re = getTableNoRe(self.config, self.site, tournament = self.tournament) elif table_name is not None: - # search_string = table_name self.name = table_name + self.type = "cash" self.tournament = None + table_kwargs = dict(table_name = table_name) + else: return None - self.find_table_parameters(search_string) + self.search_string = getTableTitleRe(self.config, self.site, self.type, **table_kwargs) + self.find_table_parameters() def __str__(self): # __str__ method for testing - likely_attrs = ("site", "number", "title", "width", "height", "x", "y", - "tournament", "table", "gdkhandle") + likely_attrs = ("number", "title", "site", "width", "height", "x", "y", + "tournament", "table", "gdkhandle", "window", "parent", + "game", "search_string", "tableno_re") temp = 'TableWindow object\n' for a in likely_attrs: if getattr(self, a, 0): temp += " %s = %s\n" % (a, getattr(self, a)) return temp +#################################################################### +# "get" methods. These query the table and return the info to get. +# They don't change the data in the table and are generally used +# by the "check" methods. Most of the get methods are in the +# subclass because they are specific to X, Windows, etc. def get_game(self): title = self.get_window_title() - print title - for game, names in game_names.iteritems(): + if title is None: + return False + +# check for nl and pl games first, to avoid bad matches + for game, names in nlpl_game_names.iteritems(): for name in names: if name in title: return game - return None + for game, names in limit_game_names.iteritems(): + for name in names: + if name in title: + return game + return False - def check_geometry(self): + def get_table_no(self): + new_title = self.get_window_title() + if new_title is None: + return False + + mo = re.search(self.tableno_re, new_title) + if mo is not None: + return mo[1] + return False + +#################################################################### +# check_table() is meant to be called by the hud periodically to +# determine if the client has been moved or resized. check_table() +# also checks and signals if the client has been closed. + def check_table(self, hud): + result = self.check_size() + if result != False: + hud.main_window.emit(result, hud) + if result == "client_destroyed": + return True + + result = self.check_loc() + if result != False: + hud.main_window.emit(result, hud) + if result == "client_destroyed": + return True + return True + +#################################################################### +# "check" methods. They use the corresponding get method, update the +# table object and return the name of the signal to be emitted or +# False if unchanged. These do not signal for destroyed +# clients to prevent a race condition. + +# These might be called by a Window.timeout, so they must not +# return False, or the timeout will be cancelled. + def check_game(self, hud): + new_game = self.get_game() + if new_game is not None and self.game != new_game: + self.game = new_game + hud.main_window.emit("game_changed", hud) + return "game_changed" + return True + + def check_size(self): new_geo = self.get_geometry() - if new_geo is None: # window destroyed return "client_destroyed" - elif self.x != new_geo['x'] or self.y != new_geo['y']: # window moved - self.x = new_geo['x'] - self.y = new_geo['y'] - return "client_moved" - elif self.width != new_geo['width'] or self.height != new_geo['height']: # window resized self.width = new_geo['width'] self.height = new_geo['height'] return "client_resized" + return False # no change - else: return False # window not changed + def check_loc(self): + new_geo = self.get_geometry() + if new_geo is None: # window destroyed + return "client_destroyed" + if self.x != new_geo['x'] or self.y != new_geo['y']: # window moved + self.x = new_geo['x'] + self.y = new_geo['y'] + return "client_moved" + return False # no change + + def check_table_no(self, hud): + result = self.get_table_no() + if result != False and result != self.table: + self.table = result + hud.main_window.emit("table_changed", hud) + return True + def check_bad_words(self, title): for word in bad_words: if word in title: return True diff --git a/pyfpdb/Tables_Demo.py b/pyfpdb/Tables_Demo.py index 199e81c2..aff1d006 100755 --- a/pyfpdb/Tables_Demo.py +++ b/pyfpdb/Tables_Demo.py @@ -31,7 +31,6 @@ import gobject # fpdb/free poker tools modules import Configuration -from HandHistoryConverter import getTableTitleRe # get the correct module for the current os if os.name == 'posix': @@ -58,24 +57,31 @@ if __name__=="__main__": self.main_window.move(table.x + dx, table.y + dy) self.main_window.show_all() table.topify(self) + +# These are the currently defined signals. Do this in the HUD. self.main_window.connect("client_moved", self.client_moved) self.main_window.connect("client_resized", self.client_resized) self.main_window.connect("client_destroyed", self.client_destroyed) + self.main_window.connect("game_changed", self.game_changed) + self.main_window.connect("table_changed", self.table_changed) +# And these of the handlers that go with those signals. +# These would live inside the HUD code. def client_moved(self, widget, hud): self.main_window.move(self.table.x + self.dx, self.table.y + self.dy) def client_resized(self, *args): - print "client resized" + print "Client resized" def client_destroyed(self, *args): # call back for terminating the main eventloop + print "Client destroyed." gtk.main_quit() - def check_on_table(table, hud): - result = table.check_geometry() - if result != False: - hud.main_window.emit(result, hud) - return True + def game_changed(self, *args): + print "Game Changed." + + def table_changed(self, *args): + print "Table Changed." print "enter table name to find: ", table_name = sys.stdin.readline() @@ -92,16 +98,12 @@ if __name__=="__main__": type = "cash" table_kwargs = dict(table_name = table_name) - search_string = getTableTitleRe(config, "Full Tilt Poker", type, **table_kwargs) - table = Tables.Table(search_string, **table_kwargs) - table.gdk_handle = gtk.gdk.window_foreign_new(table.number) - - print "table =", table -# print "game =", table.get_game() + table = Tables.Table(config, "Full Tilt Poker", **table_kwargs) + print table fake = fake_hud(table) - print "fake =", fake - gobject.timeout_add(100, check_on_table, table, fake) + gobject.timeout_add(1000, table.check_game, fake) + gobject.timeout_add(100, table.check_table, fake) print "calling main" gtk.main() diff --git a/pyfpdb/XTables.py b/pyfpdb/XTables.py index 03b18073..10626950 100644 --- a/pyfpdb/XTables.py +++ b/pyfpdb/XTables.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -"""XTables.py - -XWindows specific methods for TableWindows Class. +"""XWindows specific methods for TableWindows Class. """ # Copyright 2008 - 2010, Ray E. Barker @@ -23,73 +21,67 @@ XWindows specific methods for TableWindows Class. # Standard Library modules import re +import os +import subprocess # pyGTK modules import gtk # Other Library modules import Xlib.display -from Xlib import Xatom -# FreePokerTools modules +# FPDB modules from TableWindow import Table_Window # We might as well do this once and make them globals disp = Xlib.display.Display() root = disp.screen().root -name_atom = disp.get_atom("WM_NAME", 1) -icon_atom = disp.get_atom("WM_ICON_NAME", 1) class Table(Table_Window): - def find_table_parameters(self, search_string): - self.window = None - done_looping = False - for outside in root.query_tree().children: - for inside in outside.query_tree().children: - if done_looping: break -# if inside.get_wm_name() and re.search(search_string, inside.get_wm_name()): - if inside.get_wm_name() and re.search(search_string, inside.get_wm_icon_name()): - print "inside = ", inside - print "\n".join(dir(inside)) - for atom in inside.list_properties(): print atom, disp.get_atom_name(atom), inside.get_full_property(atom, Xatom.STRING).value - if hasattr(inside, "window"): print "window = ", str(inside.window) - if self.check_bad_words(inside.get_wm_name()): continue - self.window = inside - self.parent = outside - done_looping = True - break + def find_table_parameters(self): - if self.window == None or self.parent == None: - print "Window %s not found. Skipping." % search_string - return None - - my_geo = self.window.get_geometry() - pa_geo = self.parent.get_geometry() - - self.x = pa_geo.x + my_geo.x - self.y = pa_geo.y + my_geo.y - self.width = my_geo.width - self.height = my_geo.height - self.exe = self.window.get_wm_class()[0] - self.title = self.window.get_wm_name() - self.site = "" - self.hud = None - self.number = self.get_xid() + self.number = None + for listing in os.popen('xwininfo -root -tree').readlines(): + if re.search(self.search_string, listing): + mo = re.match('\s+([\dxabcdef]+) (.+):\s\(\"([a-zA-Z0-9\-.]+)\".+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', listing) + self.number = int( mo.group(1), 0) + self.title = re.sub('\"', '', mo.group(2)) + self.exe = "" # not used? + self.hud = None # specified later + break - def get_xid(self): - mo = re.match('Xlib\.display\.Window\(([\dxabcdef]+)', str(self.window)) - if not mo: + if self.number is None: return None - else: - return int( mo.group(1), 0) + + self.window = self.get_window_from_xid(self.number) + self.parent = self.window.query_tree().parent + + geo = self.get_geometry() + if geo is None: return None + self.width = geo['width'] + self.height = geo['height'] + self.x = geo['x'] + self.y = geo['y'] + + self.game = self.get_game() + self.gdk_handle = gtk.gdk.window_foreign_new(self.number) + + def get_window_from_xid(self, id): + for outside in root.query_tree().children: + if outside.id == id: + return outside + for inside in outside.query_tree().children: + if inside.id == id: + return inside + return None def get_geometry(self): try: my_geo = self.window.get_geometry() pa_geo = self.parent.get_geometry() - return {'x' : pa_geo.x + my_geo.x, - 'y' : pa_geo.y + my_geo.y, + return {'x' : my_geo.x + pa_geo.x, + 'y' : my_geo.y + pa_geo.y, 'width' : my_geo.width, 'height' : my_geo.height } @@ -97,8 +89,14 @@ class Table(Table_Window): return None def get_window_title(self): - return self.window.get_wm_name() - + proc = subprocess.Popen("xwininfo -wm -id %d" % self.number, shell = True, stdout = subprocess.PIPE) + s = proc.stdout.read() + mo = re.search('"(.+)"', s) + try: + return mo.group(1) + except AttributeError: + return None + def topify(self, hud): hud.main_window.gdkhandle = gtk.gdk.window_foreign_new(hud.main_window.window.xid) hud.main_window.gdkhandle.set_transient_for(self.gdk_handle) From 582377f5199ea66856ff4d673430dc8467db00f9 Mon Sep 17 00:00:00 2001 From: Eratosthenes Date: Wed, 8 Sep 2010 20:35:59 -0400 Subject: [PATCH 04/33] Catch up WinTables.py to recent changes in TableWindow.py. --- pyfpdb/TableWindow.py | 2 +- pyfpdb/WinTables.py | 21 ++++++--------------- pyfpdb/XTables.py | 4 +++- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/pyfpdb/TableWindow.py b/pyfpdb/TableWindow.py index a77a9aaa..637f2e01 100644 --- a/pyfpdb/TableWindow.py +++ b/pyfpdb/TableWindow.py @@ -57,7 +57,7 @@ limit_game_names = { #fpdb name Stars Name FTP Name # A window title might have our table name + one of these words/ # phrases. If it has this word in the title, it is not a table. -bad_words = ('History for table:', 'HUD:', 'Chat:') +bad_words = ('History for table:', 'HUD:', 'Chat:', 'FPDBHUD') # Here are the custom signals we define for allowing the 'client watcher' # thread to communicate with the gui thread. Any time a poker client is diff --git a/pyfpdb/WinTables.py b/pyfpdb/WinTables.py index 42bb8b41..638cb5b5 100644 --- a/pyfpdb/WinTables.py +++ b/pyfpdb/WinTables.py @@ -1,9 +1,7 @@ #!/usr/bin/env python -"""WinTables.py - -Routines for detecting and handling poker client windows for MS Windows. +"""Routines for detecting and handling poker client windows for MS Windows. """ -# Copyright 2008 - 2009, Ray E. Barker +# Copyright 2008 - 2010, Ray E. Barker # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -49,24 +47,19 @@ tb_height = 29 class Table(Table_Window): - def find_table_parameters(self, search_string): + def find_table_parameters(self): """Finds poker client window with the given table name.""" titles = {} win32gui.EnumWindows(win_enum_handler, titles) for hwnd in titles: if titles[hwnd] == "": continue - # print "searching ", search_string, " in ", titles[hwnd] - if re.search(search_string, titles[hwnd]): - if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window - if 'HUD:' in titles[hwnd]: continue # FPDB HUD window - if 'Chat:' in titles[hwnd]: continue # Some sites (FTP? PS? Others?) have seperable or seperately constructed chat windows - if 'FPDBHUD' in titles[hwnd]: continue # can't attach to ourselves! - self.window = hwnd + if re.search(self.search_string, titles[hwnd]): + if self.check_bad_words(titles[hwnd]): continue break try: if self.window == None: - log.error( "Window %s not found. Skipping." % search_string ) + log.error( "Window %s not found. Skipping." % self.search_string ) return None except AttributeError: log.error( "self.window doesn't exist? why?" ) @@ -79,8 +72,6 @@ class Table(Table_Window): self.width = width - x self.height = height - y log.debug("x = %s y = %s width = %s height = %s" % (self.x, self.y, self.width, self.height)) - #self.height = int(height) - b_width - tb_height - #self.width = int(width) - 2*b_width self.exe = self.get_nt_exe(hwnd) self.title = titles[hwnd] diff --git a/pyfpdb/XTables.py b/pyfpdb/XTables.py index 10626950..722547e5 100644 --- a/pyfpdb/XTables.py +++ b/pyfpdb/XTables.py @@ -45,8 +45,10 @@ class Table(Table_Window): for listing in os.popen('xwininfo -root -tree').readlines(): if re.search(self.search_string, listing): mo = re.match('\s+([\dxabcdef]+) (.+):\s\(\"([a-zA-Z0-9\-.]+)\".+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', listing) + title = re.sub('\"', '', mo.group(2)) + if self.check_bad_words(title): continue self.number = int( mo.group(1), 0) - self.title = re.sub('\"', '', mo.group(2)) + self.title = title self.exe = "" # not used? self.hud = None # specified later break From 6a129695f438aa0e162fe99998cf761e79fad6f6 Mon Sep 17 00:00:00 2001 From: Eratosthenes Date: Fri, 10 Sep 2010 17:07:59 -0400 Subject: [PATCH 05/33] Correct line removed by mistake. --- pyfpdb/WinTables.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyfpdb/WinTables.py b/pyfpdb/WinTables.py index 638cb5b5..80ae6d77 100644 --- a/pyfpdb/WinTables.py +++ b/pyfpdb/WinTables.py @@ -55,6 +55,7 @@ class Table(Table_Window): if titles[hwnd] == "": continue if re.search(self.search_string, titles[hwnd]): if self.check_bad_words(titles[hwnd]): continue + self.window = hwnd break try: From 2fc2cc9743451269b7b0276901591acf986ec17f Mon Sep 17 00:00:00 2001 From: Eratosthenes Date: Thu, 16 Sep 2010 20:29:58 -0400 Subject: [PATCH 06/33] Integrate new table handling. --- pyfpdb/HUD_main.pyw | 35 ++++++++++++++++++++---- pyfpdb/TableWindow.py | 20 ++++++++++---- pyfpdb/WinTables.py | 62 ++++++++++++++++++------------------------- pyfpdb/XTables.py | 18 +++---------- 4 files changed, 74 insertions(+), 61 deletions(-) diff --git a/pyfpdb/HUD_main.pyw b/pyfpdb/HUD_main.pyw index 590f9291..c3ac5d18 100755 --- a/pyfpdb/HUD_main.pyw +++ b/pyfpdb/HUD_main.pyw @@ -109,6 +109,11 @@ class HUD_main(object): # a main window self.main_window = gtk.Window() + self.main_window.connect("client_moved", self.client_moved) + self.main_window.connect("client_resized", self.client_resized) + self.main_window.connect("client_destroyed", self.client_destroyed) + self.main_window.connect("game_changed", self.game_changed) + self.main_window.connect("table_changed", self.table_changed) self.main_window.connect("destroy", self.destroy) self.vb = gtk.VBox() self.label = gtk.Label(_('Closing this window will exit from the HUD.')) @@ -116,11 +121,27 @@ class HUD_main(object): self.main_window.add(self.vb) self.main_window.set_title(_("HUD Main Window")) self.main_window.show_all() + gobject.timeout_add(100, self.check_tables) except: log.error( "*** Exception in HUD_main.init() *** " ) for e in traceback.format_tb(sys.exc_info()[2]): log.error(e) + def client_moved(self, widget, hud): + print hud, hud.table.name, "moved", hud.table.x, hud.table.y + + def client_resized(self, widget, hud): + print "Client resized" + + def client_destroyed(self, widget, hud): # call back for terminating the main eventloop + self.kill_hud(None, hud.table.name) + + def game_changed(self, widget, hud): + print "Game Changed." + + def table_changed(self, widget, hud): + print "Table Changed." + self.kill_hud(None, hud.table.name) def destroy(self, *args): # call back for terminating the main eventloop log.info(_("Terminating normally.")) @@ -135,6 +156,11 @@ class HUD_main(object): del(self.hud_dict[table]) self.main_window.resize(1,1) + def check_tables(self): + for hud in self.hud_dict.keys(): + self.hud_dict[hud].table.check_table(self.hud_dict[hud]) + return True + def create_HUD(self, new_hand_id, table, table_name, max, poker_game, type, stat_dict, cards): """type is "ring" or "tour" used to set hud_params""" @@ -269,6 +295,7 @@ class HUD_main(object): stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_dict[temp_key].hud_params ,self.hero_ids[site_id], num_seats) t3 = time.time() + try: self.hud_dict[temp_key].stat_dict = stat_dict except KeyError: # HUD instance has been killed off, key is stale @@ -299,10 +326,7 @@ class HUD_main(object): cards['common'] = comm_cards['common'] table_kwargs = dict(table_name = table_name, tournament = tour_number, table_number = tab_number) - search_string = getTableTitleRe(self.config, site_name, type, **table_kwargs) - # print "getTableTitleRe ", self.config, site_name, type, "=", search_string - tablewindow = Tables.Table(search_string, **table_kwargs) - + tablewindow = Tables.Table(self.config, site_name, **table_kwargs) if tablewindow is None: # If no client window is found on the screen, complain and continue if type == "tour": @@ -321,7 +345,8 @@ class HUD_main(object): log.info(_("HUD_main.read_stdin: hand read in %4.3f seconds (%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f)") % (t6-t0,t1-t0,t2-t0,t3-t0,t4-t0,t5-t0,t6-t0)) self.db_connection.connection.rollback() - + if type == "tour": + tablewindow.check_table_no() if __name__== "__main__": # start the HUD_main object diff --git a/pyfpdb/TableWindow.py b/pyfpdb/TableWindow.py index 55e1e549..0750ad75 100644 --- a/pyfpdb/TableWindow.py +++ b/pyfpdb/TableWindow.py @@ -135,6 +135,15 @@ class Table_Window(object): self.search_string = getTableTitleRe(self.config, self.site, self.type, **table_kwargs) self.find_table_parameters() + geo = self.get_geometry() + if geo is None: return None + self.width = geo['width'] + self.height = geo['height'] + self.x = geo['x'] + self.y = geo['y'] + + self.game = self.get_game() + def __str__(self): # __str__ method for testing likely_attrs = ("number", "title", "site", "width", "height", "x", "y", @@ -152,9 +161,10 @@ class Table_Window(object): # by the "check" methods. Most of the get methods are in the # subclass because they are specific to X, Windows, etc. def get_game(self): - title = self.get_window_title() - if title is None: - return False +# title = self.get_window_title() +# if title is None: +# return False + title = self.title # check for nl and pl games first, to avoid bad matches for game, names in nlpl_game_names.iteritems(): @@ -184,13 +194,13 @@ class Table_Window(object): def check_table(self, hud): result = self.check_size() if result != False: - hud.main_window.emit(result, hud) + hud.parent.main_window.emit(result, hud) if result == "client_destroyed": return True result = self.check_loc() if result != False: - hud.main_window.emit(result, hud) + hud.parent.main_window.emit(result, hud) if result == "client_destroyed": return True return True diff --git a/pyfpdb/WinTables.py b/pyfpdb/WinTables.py index ab608ea2..2e1f493d 100644 --- a/pyfpdb/WinTables.py +++ b/pyfpdb/WinTables.py @@ -79,20 +79,9 @@ class Table(Table_Window): log.error(_("self.window doesn't exist? why?")) return None - (x, y, width, height) = win32gui.GetWindowRect(hwnd) - log.debug("x = %s y = %s width = %s height = %s" % (x, y, width, height)) - self.x = int(x) + b_width - self.y = int(y) + tb_height - self.width = width - x - self.height = height - y - log.debug("x = %s y = %s width = %s height = %s" % (self.x, self.y, self.width, self.height)) - - self.exe = self.get_nt_exe(hwnd) self.title = titles[hwnd] - self.site = "" self.hud = None self.number = hwnd - self.gdkhandle = gtk.gdk.window_foreign_new(long(self.window)) def get_geometry(self): if not win32gui.IsWindow(self.number): # window closed @@ -104,8 +93,8 @@ class Table(Table_Window): height = height - y return {'x' : int(x) + b_width, 'y' : int(y) + tb_height, - 'width' : int(height) - b_width - tb_height, - 'height' : int(width) - 2*b_width + 'height' : int(height) - y, + 'width' : int(width) - x } except: return None @@ -113,26 +102,27 @@ class Table(Table_Window): def get_window_title(self): return win32gui.GetWindowText(self.window) - def get_nt_exe(self, hwnd): - """Finds the name of the executable that the given window handle belongs to.""" +# def get_nt_exe(self, hwnd): +# """Finds the name of the executable that the given window handle belongs to.""" +# +# # Request privileges to enable "debug process", so we can later use PROCESS_VM_READ, retardedly required to GetModuleFileNameEx() +# priv_flags = win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY +# hToken = win32security.OpenProcessToken (win32api.GetCurrentProcess(), priv_flags) +# # enable "debug process" +# privilege_id = win32security.LookupPrivilegeValue (None, win32security.SE_DEBUG_NAME) +# old_privs = win32security.AdjustTokenPrivileges (hToken, 0, [(privilege_id, win32security.SE_PRIVILEGE_ENABLED)]) +# +# # Open the process, and query it's filename +# processid = win32process.GetWindowThreadProcessId(hwnd) +# pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1]) +# exename = win32process.GetModuleFileNameEx(pshandle, 0) +# +# # clean up +# win32api.CloseHandle(pshandle) +# win32api.CloseHandle(hToken) +# +# return exename - # Request privileges to enable "debug process", so we can later use PROCESS_VM_READ, retardedly required to GetModuleFileNameEx() - priv_flags = win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY - hToken = win32security.OpenProcessToken (win32api.GetCurrentProcess(), priv_flags) - # enable "debug process" - privilege_id = win32security.LookupPrivilegeValue (None, win32security.SE_DEBUG_NAME) - old_privs = win32security.AdjustTokenPrivileges (hToken, 0, [(privilege_id, win32security.SE_PRIVILEGE_ENABLED)]) - - # Open the process, and query it's filename - processid = win32process.GetWindowThreadProcessId(hwnd) - pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1]) - exename = win32process.GetModuleFileNameEx(pshandle, 0) - - # clean up - win32api.CloseHandle(pshandle) - win32api.CloseHandle(hToken) - - return exename def topify(self, hud): """Set the specified gtk window to stayontop in MS Windows.""" @@ -151,10 +141,10 @@ class Table(Table_Window): # hud.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0]) hud.main_window.gdkhandle = hud.main_window.window hud.main_window.gdkhandle.set_transient_for(self.gdkhandle) - rect = self.gdkhandle.get_frame_extents() - (innerx, innery) = self.gdkhandle.get_origin() - b_width = rect.x - innerx - tb_height = rect.y - innery +# rect = self.gdkhandle.get_frame_extents() +# (innerx, innery) = self.gdkhandle.get_origin() +# b_width = rect.x - innerx +# tb_height = rect.y - innery # # style = win32gui.GetWindowLong(self.number, win32con.GWL_EXSTYLE) # style |= win32con.WS_CLIPCHILDREN diff --git a/pyfpdb/XTables.py b/pyfpdb/XTables.py index caf2251a..d8ca677f 100644 --- a/pyfpdb/XTables.py +++ b/pyfpdb/XTables.py @@ -23,7 +23,6 @@ # Standard Library modules import re import os -import subprocess # pyGTK modules import gtk @@ -50,26 +49,15 @@ class Table(Table_Window): if self.check_bad_words(title): continue self.number = int( mo.group(1), 0) self.title = title - self.exe = "" # not used? self.hud = None # specified later break - + if self.number is None: return None self.window = self.get_window_from_xid(self.number) self.parent = self.window.query_tree().parent - geo = self.get_geometry() - if geo is None: return None - self.width = geo['width'] - self.height = geo['height'] - self.x = geo['x'] - self.y = geo['y'] - - self.game = self.get_game() - self.gdk_handle = gtk.gdk.window_foreign_new(self.number) - def get_window_from_xid(self, id): for outside in root.query_tree().children: if outside.id == id: @@ -92,13 +80,13 @@ class Table(Table_Window): return None def get_window_title(self): - proc = subprocess.Popen("xwininfo -wm -id %d" % self.number, shell = True, stdout = subprocess.PIPE) - s = proc.stdout.read() + s = os.popen("xwininfo -wm -id %d" % self.number).read() mo = re.search('"(.+)"', s) try: return mo.group(1) except AttributeError: return None + def topify(self, hud): hud.main_window.gdkhandle = gtk.gdk.window_foreign_new(hud.main_window.window.xid) From 9ca3a020061b89a5fb03da70eae28216c5dfae71 Mon Sep 17 00:00:00 2001 From: Eratosthenes Date: Tue, 21 Sep 2010 23:30:57 -0400 Subject: [PATCH 07/33] Add support for draw games to example config. --- pyfpdb/HUD_config.xml.example | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/pyfpdb/HUD_config.xml.example b/pyfpdb/HUD_config.xml.example index f2ba8ce4..642b3156 100644 --- a/pyfpdb/HUD_config.xml.example +++ b/pyfpdb/HUD_config.xml.example @@ -609,6 +609,43 @@ Left-Drag to Move" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 5cf0cf58a13131736bde68eed6af168ca9308ecf Mon Sep 17 00:00:00 2001 From: Chaz Date: Wed, 22 Sep 2010 02:57:51 -0400 Subject: [PATCH 08/33] Updated saveActions default to False --- pyfpdb/Configuration.py | 4 ++-- pyfpdb/HUD_config.test.xml | 2 +- pyfpdb/HUD_config.xml.example | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) mode change 100755 => 100644 pyfpdb/Configuration.py diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py old mode 100755 new mode 100644 index 91a16cca..51d04b57 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -484,7 +484,7 @@ class Import: self.callFpdbHud = node.getAttribute("callFpdbHud") self.hhArchiveBase = node.getAttribute("hhArchiveBase") self.hhBulkPath = node.getAttribute("hhBulkPath") - self.saveActions = string_to_bool(node.getAttribute("saveActions"), default=True) + self.saveActions = string_to_bool(node.getAttribute("saveActions"), default=False) self.fastStoreHudCache = string_to_bool(node.getAttribute("fastStoreHudCache"), default=False) self.saveStarsHH = string_to_bool(node.getAttribute("saveStarsHH"), default=False) @@ -1263,7 +1263,7 @@ class Config: except: imp['hhBulkPath'] = "" try: imp['saveActions'] = self.imp.saveActions - except: imp['saveActions'] = True + except: imp['saveActions'] = False try: imp['saveStarsHH'] = self.imp.saveStarsHH except: imp['saveStarsHH'] = False diff --git a/pyfpdb/HUD_config.test.xml b/pyfpdb/HUD_config.test.xml index 94aefa82..5ec16a56 100644 --- a/pyfpdb/HUD_config.test.xml +++ b/pyfpdb/HUD_config.test.xml @@ -2,7 +2,7 @@ - +