wayland try 1
This commit is contained in:
@@ -7,6 +7,7 @@ files = [
|
|||||||
"core/headers.py",
|
"core/headers.py",
|
||||||
"core/http_share.py",
|
"core/http_share.py",
|
||||||
"core/updater.py",
|
"core/updater.py",
|
||||||
|
"core/wayland_utils.py",
|
||||||
"core/web_search.py",
|
"core/web_search.py",
|
||||||
"strings/en.json",
|
"strings/en.json",
|
||||||
"strings/personality_en.json",
|
"strings/personality_en.json",
|
||||||
|
|||||||
107
core/wayland_utils.py
Normal file
107
core/wayland_utils.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from typing import Tuple, Optional
|
||||||
|
|
||||||
|
def is_wayland() -> bool:
|
||||||
|
session_type = os.environ.get('XDG_SESSION_TYPE', '').lower()
|
||||||
|
wayland_display = os.environ.get('WAYLAND_DISPLAY', '')
|
||||||
|
|
||||||
|
return session_type == 'wayland' or bool(wayland_display)
|
||||||
|
|
||||||
|
def get_screen_info() -> Tuple[int, int]:
|
||||||
|
if is_wayland():
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(['wlr-randr'], text=True, stderr=subprocess.DEVNULL)
|
||||||
|
for line in output.splitlines():
|
||||||
|
if 'current' in line.lower():
|
||||||
|
parts = line.split()
|
||||||
|
for part in parts:
|
||||||
|
if 'x' in part and part.replace('x', '').replace('px', '').isdigit():
|
||||||
|
dims = part.replace('px', '').split('x')
|
||||||
|
return int(dims[0]), int(dims[1])
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError, PermissionError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(['swaymsg', '-t', 'get_outputs'], text=True)
|
||||||
|
outputs = json.loads(output)
|
||||||
|
if outputs and outputs[0].get('current_mode'):
|
||||||
|
mode = outputs[0]['current_mode']
|
||||||
|
return mode['width'], mode['height']
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError, json.JSONDecodeError, KeyError, PermissionError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(['kscreen-doctor', '-o'], text=True)
|
||||||
|
for line in output.splitlines():
|
||||||
|
if 'Output:' in line:
|
||||||
|
continue
|
||||||
|
if 'x' in line and '@' in line:
|
||||||
|
resolution = line.split('@')[0].strip().split()[-1]
|
||||||
|
if 'x' in resolution:
|
||||||
|
dims = resolution.split('x')
|
||||||
|
return int(dims[0]), int(dims[1])
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError, PermissionError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(['xrandr'], text=True, stderr=subprocess.DEVNULL)
|
||||||
|
for line in output.splitlines():
|
||||||
|
if ' connected' in line and 'primary' in line:
|
||||||
|
parts = line.split()
|
||||||
|
for part in parts:
|
||||||
|
if 'x' in part and '+' in part:
|
||||||
|
dims = part.split('+')[0].split('x')
|
||||||
|
return int(dims[0]), int(dims[1])
|
||||||
|
elif ' connected' in line and '*' in line:
|
||||||
|
parts = line.split()
|
||||||
|
for i, part in enumerate(parts):
|
||||||
|
if 'x' in part and i > 0:
|
||||||
|
dims = part.split('x')
|
||||||
|
if dims[0].isdigit():
|
||||||
|
return int(dims[0]), int(dims[1].split('+')[0] if '+' in dims[1] else dims[1])
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError, PermissionError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# maybe somehow right sometimes
|
||||||
|
return 1920, 1080
|
||||||
|
|
||||||
|
def set_window_bottom_right_wayland(window, width: int, height: int):
|
||||||
|
screen_w, screen_h = get_screen_info()
|
||||||
|
x = screen_w - width
|
||||||
|
y = screen_h - height
|
||||||
|
|
||||||
|
try:
|
||||||
|
window.move(x, y)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_wayland_compositor() -> Optional[str]:
|
||||||
|
desktop = os.environ.get('XDG_CURRENT_DESKTOP', '').lower()
|
||||||
|
|
||||||
|
if 'sway' in desktop:
|
||||||
|
return 'sway'
|
||||||
|
elif 'kde' in desktop or 'plasma' in desktop:
|
||||||
|
return 'kwin'
|
||||||
|
elif 'gnome' in desktop:
|
||||||
|
return 'mutter'
|
||||||
|
elif 'hypr' in desktop:
|
||||||
|
return 'hyprland'
|
||||||
|
|
||||||
|
# detect from process list
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(['ps', 'aux'], text=True)
|
||||||
|
if 'sway' in output:
|
||||||
|
return 'sway'
|
||||||
|
elif 'kwin_wayland' in output:
|
||||||
|
return 'kwin'
|
||||||
|
elif 'gnome-shell' in output:
|
||||||
|
return 'mutter'
|
||||||
|
elif 'Hyprland' in output:
|
||||||
|
return 'hyprland'
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return None
|
||||||
7
main.py
7
main.py
@@ -69,12 +69,7 @@ def main():
|
|||||||
dukto_handler.initialize()
|
dukto_handler.initialize()
|
||||||
dukto_handler.say_hello()
|
dukto_handler.say_hello()
|
||||||
|
|
||||||
# bottom right corner
|
pet.position_bottom_right()
|
||||||
screen_geometry = app.primaryScreen().availableGeometry()
|
|
||||||
pet_geometry = pet.frameGeometry()
|
|
||||||
x = screen_geometry.width() - pet_geometry.width()
|
|
||||||
y = screen_geometry.height() - pet_geometry.height()
|
|
||||||
pet.move(x, y)
|
|
||||||
|
|
||||||
pet.show()
|
pet.show()
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from core.file_search import find
|
|||||||
from core.web_search import MullvadLetaWrapper
|
from core.web_search import MullvadLetaWrapper
|
||||||
from core.http_share import FileShareServer
|
from core.http_share import FileShareServer
|
||||||
from core.config import Config
|
from core.config import Config
|
||||||
|
from core.wayland_utils import is_wayland, get_screen_info
|
||||||
|
|
||||||
from windows.app_launcher import AppLauncherDialog
|
from windows.app_launcher import AppLauncherDialog
|
||||||
from windows.file_search import FileSearchResults
|
from windows.file_search import FileSearchResults
|
||||||
@@ -46,10 +47,21 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.config = config
|
self.config = config
|
||||||
self.listener = None
|
self.listener = None
|
||||||
|
self.is_wayland = is_wayland()
|
||||||
|
|
||||||
self.restart = restart
|
self.restart = restart
|
||||||
self.no_quit = no_quit
|
self.no_quit = no_quit
|
||||||
|
|
||||||
|
# Configure window flags based on display server
|
||||||
|
if self.is_wayland:
|
||||||
|
# Wayland-compatible flags
|
||||||
|
flags = (
|
||||||
|
QtCore.Qt.FramelessWindowHint #type: ignore
|
||||||
|
| QtCore.Qt.WindowStaysOnTopHint #type: ignore
|
||||||
|
| QtCore.Qt.Tool #type: ignore
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# X11 flags
|
||||||
flags = (
|
flags = (
|
||||||
QtCore.Qt.FramelessWindowHint #type: ignore
|
QtCore.Qt.FramelessWindowHint #type: ignore
|
||||||
| QtCore.Qt.WindowStaysOnTopHint #type: ignore
|
| QtCore.Qt.WindowStaysOnTopHint #type: ignore
|
||||||
@@ -59,6 +71,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
|
|
||||||
self.setWindowFlags(flags)
|
self.setWindowFlags(flags)
|
||||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground) #type: ignore
|
self.setAttribute(QtCore.Qt.WA_TranslucentBackground) #type: ignore
|
||||||
|
|
||||||
|
# On Wayland, set additional window properties to prevent Alt+Tab
|
||||||
|
if self.is_wayland:
|
||||||
|
self.setAttribute(QtCore.Qt.WA_X11NetWmWindowTypeUtility) #type: ignore
|
||||||
|
|
||||||
pix = QtGui.QPixmap(str(ASSET))
|
pix = QtGui.QPixmap(str(ASSET))
|
||||||
|
|
||||||
self.label = QtWidgets.QLabel(self)
|
self.label = QtWidgets.QLabel(self)
|
||||||
@@ -119,6 +136,22 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
if config.get("hotkey") != None and config.get("hotkey") != "none" and config.get("hotkey") != "":
|
if config.get("hotkey") != None and config.get("hotkey") != "none" and config.get("hotkey") != "":
|
||||||
self.start_hotkey_listener()
|
self.start_hotkey_listener()
|
||||||
|
|
||||||
|
def position_bottom_right(self):
|
||||||
|
"""Position window at bottom right corner."""
|
||||||
|
if self.is_wayland:
|
||||||
|
# On Wayland, get screen info and position window
|
||||||
|
screen_w, screen_h = get_screen_info()
|
||||||
|
x = screen_w - self.width()
|
||||||
|
y = screen_h - self.height()
|
||||||
|
self.move(x, y)
|
||||||
|
else:
|
||||||
|
# On X11, use Qt's screen geometry
|
||||||
|
screen_geometry = QtWidgets.QApplication.primaryScreen().availableGeometry()
|
||||||
|
pet_geometry = self.frameGeometry()
|
||||||
|
x = screen_geometry.width() - pet_geometry.width()
|
||||||
|
y = screen_geometry.height() - pet_geometry.height()
|
||||||
|
self.move(x, y)
|
||||||
|
|
||||||
def build_menus(self):
|
def build_menus(self):
|
||||||
s = self.strings["main_window"]["right_menu"]
|
s = self.strings["main_window"]["right_menu"]
|
||||||
|
|
||||||
@@ -251,10 +284,15 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
def ensure_on_top(self):
|
def ensure_on_top(self):
|
||||||
if self.isVisible() and not self.left_menu.isVisible() and not self.tray.contextMenu().isVisible():
|
if self.isVisible() and not self.left_menu.isVisible() and not self.tray.contextMenu().isVisible():
|
||||||
self.raise_()
|
self.raise_()
|
||||||
|
# Re-apply window flags to ensure staying on top on Wayland
|
||||||
|
if self.is_wayland:
|
||||||
|
self.setWindowFlag(QtCore.Qt.WindowStaysOnTopHint, True) #type: ignore
|
||||||
|
|
||||||
def showEvent(self, event):
|
def showEvent(self, event):
|
||||||
super().showEvent(event)
|
super().showEvent(event)
|
||||||
self.raise_()
|
self.raise_()
|
||||||
|
# Ensure bottom right positioning
|
||||||
|
QtCore.QTimer.singleShot(100, self.position_bottom_right)
|
||||||
|
|
||||||
def mousePressEvent(self, event: QtGui.QMouseEvent):
|
def mousePressEvent(self, event: QtGui.QMouseEvent):
|
||||||
if event.button() == QtCore.Qt.LeftButton: #type: ignore
|
if event.button() == QtCore.Qt.LeftButton: #type: ignore
|
||||||
|
|||||||
Reference in New Issue
Block a user