Merge branch 'Xlib'

Conflicts:
	pyfpdb/TableWindow.py
	pyfpdb/Tables_Demo.py
	pyfpdb/WinTables.py
	pyfpdb/XTables.py
This commit is contained in:
Eratosthenes 2010-09-10 23:01:21 -04:00
commit d176da93d4
5 changed files with 218 additions and 151 deletions

View File

@ -669,12 +669,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

View File

@ -1,10 +1,12 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""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 - 2010, Ray E. Barker
@ -25,37 +27,38 @@ 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:')
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
@ -76,11 +79,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.
@ -92,60 +103,142 @@ 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:

View File

@ -25,16 +25,13 @@ 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
# fpdb/free poker tools modules
import Configuration
from HandHistoryConverter import getTableTitleRe
import locale
lang=locale.getdefaultlocale()[0][0:2]
@ -73,24 +70,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()
@ -107,16 +111,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)
print _("calling main")
gobject.timeout_add(1000, table.check_game, fake)
gobject.timeout_add(100, table.check_table, fake)
print "calling main"
gtk.main()

View File

@ -1,8 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""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 - 2010, Ray E. Barker
@ -62,24 +60,20 @@ 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!
if re.search(self.search_string, titles[hwnd]):
if self.check_bad_words(titles[hwnd]): continue
self.window = hwnd
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?"))
@ -92,8 +86,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]

View File

@ -1,10 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Discover_Tables.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 - 2010, Ray E. Barker
@ -27,99 +23,68 @@ of Table_Window objects representing the windows found.
# Standard Library modules
import re
import os
import subprocess
# pyGTK modules
import pygtk
import gtk
# Other Library modules
import Xlib
import Xlib.display
# 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)
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
def find_table_parameters(self):
window_number = None
self.number = None
for listing in os.popen('xwininfo -root -tree').readlines():
if re.search(search_string, listing):
# print listing
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.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.title = title
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
self.hud = None # specified later
break
if window_number is None:
if self.number is None:
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.window = self.get_window_from_xid(self.number)
self.parent = self.window.query_tree().parent
# window_string = str(self.window)
mo = re.match('Xlib\.display\.Window\(([\dxabcdef]+)', window_string)
if not mo:
print "Not matched"
self.gdk_handle = None
else:
self.number = int( mo.group(1), 0)
print "number =", self.number
# self.gdk_handle = gtk.gdk.window_foreign_new(int(self.number))
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
}
@ -127,7 +92,13 @@ 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)