Coverage for WinTables.py: 34%

121 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-14 11:07 +0000

1import ctypes 

2import re 

3import logging 

4from ctypes import wintypes 

5from PyQt5.QtGui import QWindow 

6from PyQt5.QtCore import Qt 

7from PyQt5.QtWidgets import QApplication 

8import sys 

9import time 

10 

11from TableWindow import Table_Window 

12 

13app = QApplication(sys.argv) 

14 

15# logging setup 

16log = logging.getLogger("hud") 

17 

18# Definition of Windows API constants 

19GW_OWNER = 4 

20GWL_EXSTYLE = -20 

21WS_EX_TOOLWINDOW = 0x00000080 

22WS_EX_APPWINDOW = 0x00040000 

23SM_CXSIZEFRAME = 32 

24SM_CYCAPTION = 4 

25 

26# Windows functions via ctypes 

27EnumWindows = ctypes.windll.user32.EnumWindows 

28EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.wintypes.HWND, ctypes.wintypes.LPARAM) 

29GetWindowText = ctypes.windll.user32.GetWindowTextW 

30GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW 

31IsWindowVisible = ctypes.windll.user32.IsWindowVisible 

32GetParent = ctypes.windll.user32.GetParent 

33GetWindowRect = ctypes.windll.user32.GetWindowRect 

34GetWindowLong = ctypes.windll.user32.GetWindowLongW 

35GetSystemMetrics = ctypes.windll.user32.GetSystemMetrics 

36IsWindow = ctypes.windll.user32.IsWindow 

37MoveWindow = ctypes.windll.user32.MoveWindow 

38 

39# Global variables 

40b_width = 3 

41tb_height = 29 

42 

43 

44# Class for temporarily storing securities 

45class WindowInfoTemp: 

46 def __init__(self): 

47 self.titles = {} 

48 # print("WindowInfo initialized with an empty dictionary.") 

49 

50 

51# Function for listing windows and retrieving titles 

52def win_enum_handler(hwnd, lParam): 

53 # print(f"Handler called for hwnd: {hwnd}") 

54 window_info = ctypes.cast(lParam, ctypes.py_object).value 

55 length = GetWindowTextLength(hwnd) 

56 # print(f"Window text length: {length}") 

57 if length > 0: 

58 buff = ctypes.create_unicode_buffer(length + 1) 

59 GetWindowText(hwnd, buff, length + 1) 

60 # print(f"Text retrieved for hwnd {hwnd}: {buff.value}") 

61 window_info.titles[hwnd] = buff.value 

62 return True 

63 

64 

65class Table(Table_Window): 

66 # In find_table_parameters of WinTables.py 

67 

68 def find_table_parameters(self): 

69 """Find a poker client window with the given table name.""" 

70 window_info = WindowInfoTemp() 

71 

72 try: 

73 log.debug("before EnumWindows") 

74 EnumWindows(EnumWindowsProc(win_enum_handler), ctypes.py_object(window_info)) 

75 log.debug(f"after EnumWindows found {len(window_info.titles)} windows") 

76 except Exception as e: 

77 log.error(f"Error during EnumWindows: {e}") 

78 

79 time_limit = 10 # Limite de temps en secondes 

80 start_time = time.time() # Enregistre l'heure de début 

81 

82 for hwnd in window_info.titles: 

83 try: 

84 if time.time() - start_time > time_limit: 

85 log.error(f"Time limit of {time_limit} seconds reached. Exiting loop.") 

86 break 

87 

88 title = window_info.titles[hwnd] 

89 if not title: 

90 continue 

91 

92 if not IsWindowVisible(hwnd): 

93 continue 

94 if GetParent(hwnd) != 0: 

95 continue 

96 

97 # HasNoOwner = ctypes.windll.user32.GetWindow(hwnd, GW_OWNER) == 0 

98 # WindowStyle = GetWindowLong(hwnd, GWL_EXSTYLE) 

99 

100 if title.split(" ", 1)[0] == "Winamax": 

101 self.search_string = self.search_string.split(" ", 3)[0] 

102 

103 if re.search(self.search_string, title, re.I): 

104 if self.check_bad_words(title): 

105 continue 

106 self.number = hwnd 

107 self.title = title 

108 log.debug(f"Found table in hwnd {self.number} title {self.title}") 

109 break 

110 

111 except IOError as e: 

112 if "closed file" in str(e): 

113 print(f"Warning: Logging to a closed file for hwnd {hwnd}. Skipping this log entry.") 

114 else: 

115 log.error(f"IOError for hwnd {hwnd}: {e}") 

116 except Exception as e: 

117 log.error(f"Unexpected error for hwnd {hwnd}: {e}") 

118 

119 if self.number is None: 

120 log.error(f"Window {self.search_string} not found.") 

121 

122 # In get_geometry of WinTables.py 

123 def get_geometry(self): 

124 """Get the window geometry.""" 

125 # print(f"Attempting to retrieve geometry for hwnd: {self.number}") 

126 try: 

127 rect = wintypes.RECT() 

128 if IsWindow(self.number): 

129 result = GetWindowRect(self.number, ctypes.byref(rect)) 

130 if result != 0: 

131 x, y = rect.left, rect.top 

132 width = rect.right - rect.left 

133 height = rect.bottom - rect.top 

134 

135 self.b_width = GetSystemMetrics(SM_CXSIZEFRAME) 

136 self.tb_height = GetSystemMetrics(SM_CYCAPTION) 

137 

138 # print(f"Position: ({x}, {y}), Dimensions: {width}x{height}") 

139 # print(f"Border width: {self.b_width}, Title bar height: {self.tb_height}") 

140 

141 return { 

142 "x": int(x) + self.b_width, 

143 "y": int(y) + self.tb_height + self.b_width, 

144 "height": int(height) - 2 * self.b_width - self.tb_height, 

145 "width": int(width) - 2 * self.b_width, 

146 } 

147 else: 

148 log.error(f"Failed to retrieve GetWindowRect for hwnd: {self.number}") 

149 return None 

150 else: 

151 log.error(f"The window {self.number} is not valid.") 

152 return None 

153 except Exception as e: 

154 log.error(f"Error retrieving geometry: {e}") 

155 return None 

156 

157 def get_window_title(self): 

158 log.debug(f"title for {self.number}") 

159 length = GetWindowTextLength(self.number) 

160 buff = ctypes.create_unicode_buffer(length + 1) 

161 GetWindowText(self.number, buff, length + 1) 

162 log.debug(f"title {buff.value}") 

163 return buff.value 

164 

165 def move_and_resize_window(self, x, y, width, height): 

166 """Move and resize the specified window.""" 

167 if self.number: 

168 # print(f"Moving and resizing window {self.number}") 

169 MoveWindow(self.number, x, y, width, height, True) 

170 # print(f"Window moved to ({x}, {y}) with dimensions {width}x{height}") 

171 else: 

172 log.info("No window to move.") 

173 

174 def topify(self, window): 

175 """Make the specified Qt window 'always on top' under Windows.""" 

176 if self.gdkhandle is None: 

177 self.gdkhandle = QWindow.fromWinId(int(self.number)) 

178 

179 qwindow = window.windowHandle() 

180 qwindow.setTransientParent(self.gdkhandle) 

181 qwindow.setFlags(Qt.Tool | Qt.FramelessWindowHint | Qt.WindowDoesNotAcceptFocus | Qt.WindowStaysOnTopHint) 

182 

183 def check_bad_words(self, title): 

184 """Check if the title contains any bad words.""" 

185 bad_words = ["History for table:", "HUD:", "Chat:", "FPDBHUD", "Lobby"] 

186 return any(bad_word in title.lower() for bad_word in bad_words)