Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ on:

jobs:
build:
runs-on: windows-latest
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v4

Expand All @@ -28,14 +31,20 @@ jobs:

- name: Build with PyInstaller
run: |
pyinstaller --onefile --noconsole --name Baafucha --add-data "assets/icon.png:assets" --icon=assets/icon.png main.py
if [ ${{ matrix.os }} == 'windows-latest' ]; then
pyinstaller --onefile --noconsole --name Baafucha --add-data "assets/icon.png:assets" --icon=assets/icon.png main.py
elif [ ${{ matrix.os }} == 'macos-latest' ]; then
pyinstaller --onefile --noconsole --name Baafucha --add-data "assets/icon.png:assets" --icon=assets/icon.png main.py
fi

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: Baafucha
path: dist/Baafucha.exe

name: Baafucha-${{ matrix.os }}
path: |
dist/Baafucha.exe
dist/Baafucha

release:
needs: build
runs-on: ubuntu-latest
Expand All @@ -45,7 +54,7 @@ jobs:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: Baafucha
name: Baafucha-${{ matrix.os }}

- name: Get version
id: get_version
Expand All @@ -68,6 +77,6 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./Baafucha.exe
asset_name: Baafucha.exe
asset_path: ./Baafucha-${{ matrix.os }}
asset_name: Baafucha-${{ matrix.os }}
asset_content_type: application/octet-stream
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Ba'afucha is a tiny program that provides an easy way to convert text between En

## Requirements

- Windows operating system
- Windows or MacOS operating system

## Installation

Expand All @@ -50,7 +50,7 @@ Download the latest release from the [Releases](https://github.com/matipojo/baaf

## Usage

1. If you downloaded the release version, simply run the `Baafucha.exe` file.
1. If you downloaded the release version, simply run the `Baafucha.exe` file on Windows or the `Baafucha` file on MacOS.

2. If you're running from source, run the script:

Expand All @@ -70,7 +70,7 @@ Download the latest release from the [Releases](https://github.com/matipojo/baaf

## Building from Source

To create a standalone executable for Windows:
To create a standalone executable for Windows or MacOS:

1. Ensure you have PyInstaller installed:

Expand All @@ -84,7 +84,7 @@ To create a standalone executable for Windows:
pyinstaller --onefile --noconsole --name Baafucha baafucha.py
```

3. Find the `Baafucha.exe` in the `dist` folder.
3. Find the `Baafucha.exe` in the `dist` folder for Windows or the `Baafucha` file for MacOS.

## Continuous Integration

Expand Down
238 changes: 116 additions & 122 deletions core/taskbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,133 +7,127 @@
the language changes.
"""

import ctypes
import multiprocessing
import time
import winreg
import threading
import platform
from core.tray import load_config

# Loading the library user32.dll
user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32

# Gets the handle of the taskbar
taskbar_handle = user32.FindWindowW("Shell_TrayWnd", None)

# Setting constants
WM_SETTINGCHANGE = 0x001A

import winreg

REGISTRY_PATH = r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"
VALUE_NAME = "ColorPrevalence"

def get_set_color_prevalence(set_value=None):
"""
Gets or sets the ColorPrevalence value in the registry.
If set_value is None, it returns the current value.
If set_value is provided, it sets the new value and returns it.
"""

try:
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, REGISTRY_PATH, 0, winreg.KEY_READ | winreg.KEY_WRITE) as key:
if set_value is None:
value, _ = winreg.QueryValueEx(key, VALUE_NAME)
return value
else:
winreg.SetValueEx(key, VALUE_NAME, 0, winreg.REG_DWORD, set_value)
return set_value
except Exception as e:
print(f"An error occurred with ColorPrevalence: {e}")
return None

def refresh_taskbar():
"""
Refreshes the taskbar by sending a settings change notification to the taskbar.
"""
user32.SendMessageW(taskbar_handle, WM_SETTINGCHANGE, 0, "ImmersiveColorSet")

def set_color_prevalence(value):
"""
Sets the ColorPrevalence value in the registry and refreshes the taskbar.
"""
get_set_color_prevalence(value)
refresh_taskbar()

def get_current_input_language():
# Get the foreground window
hwnd = user32.GetForegroundWindow()

# Get the thread of the foreground window
thread_id = user32.GetWindowThreadProcessId(hwnd, 0)

# Get the keyboard layout of the thread
layout_id = user32.GetKeyboardLayout(thread_id)

# Extract the language ID from the keyboard layout
language_id = layout_id & 0xFFFF

return language_id

language_monitor_thread = None
language_monitor_stop_event = None
class Taskbar:
def start_language_monitor(self):
raise NotImplementedError

def stop_language_monitor(self):
raise NotImplementedError

def on_config_change(self, new_config):
raise NotImplementedError

if platform.system() == "Windows":
import ctypes
import threading
import time
import winreg

class WindowsTaskbar(Taskbar):
def __init__(self):
self.user32 = ctypes.windll.user32
self.kernel32 = ctypes.windll.kernel32
self.taskbar_handle = self.user32.FindWindowW("Shell_TrayWnd", None)
self.WM_SETTINGCHANGE = 0x001A
self.REGISTRY_PATH = r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"
self.VALUE_NAME = "ColorPrevalence"
self.language_monitor_thread = None
self.language_monitor_stop_event = None

def get_set_color_prevalence(self, set_value=None):
try:
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, self.REGISTRY_PATH, 0, winreg.KEY_READ | winreg.KEY_WRITE) as key:
if set_value is None:
value, _ = winreg.QueryValueEx(key, self.VALUE_NAME)
return value
else:
winreg.SetValueEx(key, self.VALUE_NAME, 0, winreg.REG_DWORD, set_value)
return set_value
except Exception as e:
print(f"An error occurred with ColorPrevalence: {e}")
return None

def refresh_taskbar(self):
self.user32.SendMessageW(self.taskbar_handle, self.WM_SETTINGCHANGE, 0, "ImmersiveColorSet")

def set_color_prevalence(self, value):
self.get_set_color_prevalence(value)
self.refresh_taskbar()

def get_current_input_language(self):
hwnd = self.user32.GetForegroundWindow()
thread_id = self.user32.GetWindowThreadProcessId(hwnd, 0)
layout_id = self.user32.GetKeyboardLayout(thread_id)
language_id = layout_id & 0xFFFF
return language_id

def start_language_monitor(self):
config = load_config()
if config.get("taskbar_color", False):
self.language_monitor_stop_event = threading.Event()
self.language_monitor_thread = threading.Thread(target=self.monitor_language, args=(self.language_monitor_stop_event,))
self.language_monitor_thread.start()

def stop_language_monitor(self):
if self.language_monitor_thread:
self.set_color_prevalence(0)
self.language_monitor_stop_event.set()
self.language_monitor_thread.join()
self.language_monitor_thread = None
self.language_monitor_stop_event.clear()

def on_config_change(self, new_config):
self.stop_language_monitor()
if new_config.get("taskbar_color", False):
if not self.language_monitor_thread:
self.start_language_monitor()

def monitor_language(self, stop_event):
last_layout_id = self.get_current_input_language()
new_value = 0 if last_layout_id == self.kernel32.GetUserDefaultUILanguage() else 1
self.set_color_prevalence(new_value)
while not stop_event.is_set():
layout_id = self.get_current_input_language()
if layout_id != last_layout_id:
last_layout_id = layout_id
if layout_id == self.kernel32.GetUserDefaultUILanguage():
self.set_color_prevalence(0)
else:
self.set_color_prevalence(1)
print("Language change detected to language ID:", layout_id)
time.sleep(0.2)

elif platform.system() == "Darwin":
import subprocess

class MacTaskbar(Taskbar):
def start_language_monitor(self):
pass # Implement Mac-specific logic if needed

def stop_language_monitor(self):
pass # Implement Mac-specific logic if needed

def on_config_change(self, new_config):
pass # Implement Mac-specific logic if needed

def get_taskbar():
if platform.system() == "Windows":
return WindowsTaskbar()
elif platform.system() == "Darwin":
return MacTaskbar()
else:
raise NotImplementedError("Unsupported platform")

taskbar = get_taskbar()

def start_language_monitor():
config = load_config()

if config.get("taskbar_color", False):
global language_monitor_thread
global language_monitor_stop_event

language_monitor_stop_event = threading.Event()
language_monitor_thread = threading.Thread(target=monitor_language, args=(language_monitor_stop_event,))
language_monitor_thread.start()
taskbar.start_language_monitor()

def stop_language_monitor():
global language_monitor_thread
if language_monitor_thread:
global language_monitor_stop_event

set_color_prevalence(0)

language_monitor_stop_event.set()
language_monitor_thread.join()
language_monitor_thread = None
language_monitor_stop_event.clear()
taskbar.stop_language_monitor()

def on_config_change(new_config):
stop_language_monitor()
if new_config.get("taskbar_color", False):
if not language_monitor_thread:
start_language_monitor()

# The main process of the program
def monitor_language(stop_event):
# Stores the last language ID
last_layout_id = get_current_input_language()

# Set the initial color value
new_value = 0 if last_layout_id == kernel32.GetUserDefaultUILanguage() else 1
set_color_prevalence( new_value )

# Main loop to check language changes
while not stop_event.is_set():
layout_id = get_current_input_language()

# If a language change is detected, changes the color value and refreshes the taskbar
if layout_id != last_layout_id:
last_layout_id = layout_id

if layout_id == kernel32.GetUserDefaultUILanguage():
set_color_prevalence(0)
else:
set_color_prevalence(1)
print("Language change detected to language ID:", layout_id)

# Waits 0.2 seconds before next test
time.sleep(0.2)

# Running the main program
if __name__ == "__main__":
main()
taskbar.on_config_change(new_config)
Loading