272 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			272 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!./venv/bin/python3
 | |
| 
 | |
| """pywmreceived.py
 | |
| WindowMaker dockapp pidgin messages 
 | |
| Copyright (C) 2025 Fredrick W. Warren
 | |
| Licensed under the GNU General Public License.
 | |
| """
 | |
| 
 | |
| import logging
 | |
| from wmdocklib import wmoo as wmoo
 | |
| import dbus
 | |
| import dbus.mainloop.glib
 | |
| from gi.repository import GLib
 | |
| import threading
 | |
| import os
 | |
| from icecream import ic
 | |
| from xpm_resources import palette, background, patterns
 | |
| 
 | |
| line_height = 9
 | |
| 
 | |
| logging.basicConfig(level=logging.INFO)
 | |
| logger = logging.getLogger(__name__)
 | |
| 
 | |
| class Application(wmoo.Application):
 | |
|     """
 | |
|     Display dockapp and respond to libpurple dbus
 | |
|     messages ReceivedImMsg and SentImMsg
 | |
|     """
 | |
| 
 | |
|     def __init__(self, *args, **kwargs):
 | |
|         """
 | |
|         """
 | |
|         wmoo.Application.__init__(self, *args, **kwargs)
 | |
|         self._count = 0
 | |
|         self._flasher = 0 
 | |
|         self.backlit = 0 
 | |
|         self.lines = [ # name, messages received
 | |
|             ["  CATHY", 0],
 | |
|             ["  FRANK", 0],
 | |
|             ["  TIM", 0],
 | |
|             ["  LEE", 0],
 | |
|             ["  TANDA", 0],
 | |
|             ["  OTHER", 0],
 | |
|         ]
 | |
|         # Initialize D-Bus and connect to Pidgin's ReceivedIMMsg signal
 | |
|         self.register_dbus()
 | |
| 
 | |
|     def register_dbus(self):
 | |
|         """
 | |
|         Register im.pidgin.purple dbus to listen for messages
 | |
|         """
 | |
|         try:
 | |
|             # Set up the D-Bus main loop
 | |
|             dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 | |
| 
 | |
|             # Connect to the session bus
 | |
|             bus = dbus.SessionBus()
 | |
| 
 | |
|             # Obtain the Pidgin D-Bus service object
 | |
|             purple_service = bus.get_object(
 | |
|                 "im.pidgin.purple.PurpleService",
 | |
|                 "/im/pidgin/purple/PurpleObject"
 | |
|             )
 | |
| 
 | |
|             # Get the interface to interact with
 | |
|             purple_interface = dbus.Interface(
 | |
|                 purple_service,
 | |
|                 "im.pidgin.purple.PurpleInterface"
 | |
|             )
 | |
| 
 | |
|             # Connect the ReceivedIMMsg signal to the handler
 | |
|             purple_interface.connect_to_signal(
 | |
|                 "ReceivedImMsg",
 | |
|                 self.handle_received_im_msg
 | |
|             )
 | |
|             # Connect the SentIMMsg signal to the handler
 | |
|             purple_interface.connect_to_signal(
 | |
|                 "SentImMsg",
 | |
|                 self.handle_sent_im_msg
 | |
|             )
 | |
| 
 | |
|             ic("Connected to Pidgin's ReceivedIMMsg signal successfully.")
 | |
|         
 | |
|         except dbus.DBusException as e:
 | |
|             print("Failed to connect to Pidgin's D-Bus interface:", e)
 | |
| 
 | |
|     def handle_received_im_msg(self, account, sender, message, conversation, flags):
 | |
|         """
 | |
|         Callback function that handles the ReceivedIMMsg signal.
 | |
|         Prints the sender and message.
 | |
| 
 | |
|         Parameters:
 | |
|             account (str): The account from which the message was received.
 | |
|             sender (str): The sender's identifier.
 | |
|             message (str): The message content.
 | |
|             conversation (str): The conversation identifier.
 | |
|             flags (int): Message flags.
 | |
|         """
 | |
|         ic("")
 | |
|         ic(f"sender: {sender}")
 | |
|         ic(f"message: {message}")
 | |
|         if self.backlit:
 | |
|             self._flasher = 8
 | |
|         else:
 | |
|             self._flasher = 7
 | |
| 
 | |
|     def handle_sent_im_msg(self, account, recepient, message):
 | |
|         """
 | |
|         Callback function that handles the SentImMsg signal.
 | |
|         Prints the sender and message.
 | |
| 
 | |
|         Parameters:
 | |
|             recepient (str): The recepien's identifier.
 | |
|             message (str): The message content.
 | |
|         """
 | |
|         ic("")
 | |
|         ic(f"recepient: {recepient}")
 | |
|         ic(f"message: {message}")
 | |
|         self.clear_messages()
 | |
| 
 | |
|     def draw_string(self, xstart, ystart, text):
 | |
|         """
 | |
|         Draw text in dockapp with normal or backlit backround
 | |
| 
 | |
|         Parameters:
 | |
|             xstart (int): pixels from left edge of dockapp
 | |
|             ystart (int): pixels from top edge of dockapp
 | |
|             text (str): text to display, will be trundicated
 | |
|         """
 | |
|         for char in text:
 | |
|             if char >= "A" and char <="Z":
 | |
|                 x = (ord(char) -65) * 6
 | |
|                 y = 1
 | |
|             elif char >= "0" and char <="9":
 | |
|                 x = (ord(char) -48) * 6
 | |
|                 y = 10 
 | |
|             elif char == " ":
 | |
|                 x = 6 * 10
 | |
|                 y = 10
 | |
|             elif char == "-":
 | |
|                 x = 6 * 11
 | |
|                 y = 10
 | |
|             elif char == ".":
 | |
|                 x = 6 * 12
 | |
|                 y = 10
 | |
|             elif char == "'":
 | |
|                 x = 6 * 13
 | |
|                 y = 10
 | |
|             elif char == "(":
 | |
|                 x = 6 * 14 
 | |
|                 y = 10
 | |
|             elif char == ")":
 | |
|                 x = 6 * 15
 | |
|                 y = 10
 | |
|             elif char == "*":
 | |
|                 x = 6 * 16
 | |
|                 y = 10
 | |
|             elif char == "/":
 | |
|                 x = 6 * 17
 | |
|                 y = 10
 | |
|             else:
 | |
|                 continue
 | |
|             self.putPattern(x, y + (self.backlit * 17), 6, 7, xstart, ystart)
 | |
|             xstart += 6
 | |
| 
 | |
|     def draw_background(self):
 | |
|         """
 | |
|         Redraw background of dockapp
 | |
|         """
 | |
|         self.putPattern(0 + (self.backlit * 62), 36, 64, 64, 0, 0)
 | |
| 
 | |
|     def draw_all_text(self):
 | |
|         """
 | |
|         Redraw all text
 | |
|         """
 | |
|         for index, line in enumerate(self.lines[:6]):
 | |
|             self.draw_string(9, 6 + (index * line_height), line[0])
 | |
| 
 | |
|     def toggle_backlite(self):
 | |
|         """
 | |
|         Toggle the state of the dockapp background
 | |
|         """
 | |
|         self.backlit = 1 - self.backlit
 | |
|         self.draw_background()
 | |
|         self.draw_all_text()
 | |
| 
 | |
|     def backlite_off(self):
 | |
|         """
 | |
|         Turn off the backlight mode in response to a mouseclick
 | |
|         """
 | |
|         self._flasher = 0
 | |
|         if self.backlit:
 | |
|             self.toggle_backlite()
 | |
| 
 | |
|     def update(self):
 | |
|         """
 | |
|         Update display 
 | |
|         """
 | |
|         wmoo.Application.update(self)
 | |
|         self._count += 1
 | |
|         if self._count <= 3:
 | |
|             return
 | |
|         self._count = 0
 | |
|         if self._flasher:
 | |
|             self._flasher -= 1
 | |
|             self.toggle_backlite()
 | |
| 
 | |
|     def clear_messages(self):
 | |
|         """
 | |
|         Turn of backlite and zero out messages
 | |
|         """
 | |
|         self.backlite_off()
 | |
| 
 | |
|     def handle_buttonrelease(self, event):
 | |
|         """
 | |
|         On left click zero out recieved messages and turn off backlite
 | |
| 
 | |
|         Parameters:
 | |
|             event (dict):
 | |
|                 button (int): 1 left click, 2 middle click, 3 right click
 | |
|                 type (str): buttonrelease
 | |
|                 x (int): x position of click
 | |
|                 y (int): y position of click
 | |
|         """
 | |
|         if event['button'] == 1:
 | |
|             self.clear_messages()
 | |
| 
 | |
|     
 | |
| def run_glib_mainloop():
 | |
|     """
 | |
|     Runs the GLib main loop. This should be executed in a separate thread.
 | |
|     """
 | |
|     loop = GLib.MainLoop()
 | |
|     ic("Start Loop")
 | |
|     try:
 | |
|         loop.run()
 | |
|     except KeyboardInterrupt:
 | |
|         loop.quit()
 | |
| 
 | |
| def main():
 | |
|     """
 | |
|     The main entry point of the application.
 | |
| 
 | |
|     Parameters:
 | |
|     """
 | |
|     if os.environ.get("PRODUCTION") == "true":
 | |
|         ic.disable()
 | |
| 
 | |
|     app = Application(font_name='5x8',
 | |
|                       margin = 3,
 | |
|                       bg=0,
 | |
|                       fg=2,
 | |
|                       palette = palette,
 | |
|                       background = background,
 | |
|                       patterns = patterns)
 | |
|     # app.addCallback(app.previousRadio, 'buttonrelease', area=( 6,29,15,38))
 | |
|     # 6x7 grey1=1   grey2=10  green1=18 green2=27
 | |
|     app.draw_background()
 | |
|     app.draw_all_text()
 | |
|     app.addCallback(app.handle_buttonrelease, 'buttonrelease', area=(2,2,62,62))
 | |
| 
 | |
|     # Start the GLib main loop in a separate thread
 | |
|     glib_thread = threading.Thread(target=run_glib_mainloop, daemon=True)
 | |
|     glib_thread.start()
 | |
|     
 | |
|     # Run the application's main loop
 | |
|     app.run()
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     main()
 |