-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from D0rkKnight/progs
Launch progs from windows
- Loading branch information
Showing
13 changed files
with
383 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,4 @@ dist | |
*.spec | ||
*.egg-info | ||
.cache | ||
public/icons/programs/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
default_prog_windows.svg: https://iconoir.com/ (Windows Icon) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,5 @@ uvicorn | |
pydantic | ||
spotipy | ||
pyinstaller | ||
pillow | ||
pillow | ||
pywin32 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
import subprocess | ||
import platform | ||
import json | ||
import os | ||
import src.extract_icon as extract_icon | ||
import configparser | ||
import shutil | ||
import pythoncom | ||
|
||
from PIL import Image | ||
from src.cmd_types import * | ||
|
||
|
||
def start_app(app: str): | ||
if platform.system() == "Windows": | ||
command = f'start "" "{app}"' | ||
subprocess.Popen(command, shell=True) | ||
else: | ||
raise NotImplementedError | ||
|
||
|
||
def get_commands(): | ||
pythoncom.CoInitialize() | ||
|
||
commands = [] | ||
commands += get_prog_commands() | ||
|
||
pythoncom.CoUninitialize() | ||
return commands | ||
|
||
|
||
def get_prog_commands(): | ||
# opens start menu and gets all programs | ||
if platform.system() == "Windows": | ||
global_path = os.path.join( | ||
os.environ["PROGRAMDATA"], | ||
"Microsoft", | ||
"Windows", | ||
"Start Menu", | ||
) | ||
local_path = os.path.join( | ||
os.environ["APPDATA"], | ||
"Microsoft", | ||
"Windows", | ||
"Start Menu", | ||
) | ||
|
||
print("System Start Menu Contents:") | ||
shortcuts = list_shortcuts_windows(global_path) + list_shortcuts_windows( | ||
local_path | ||
) | ||
|
||
# print(shortcuts) | ||
|
||
# Clear out shortcuts with duplicate names | ||
names = [] | ||
unique_shortcuts = [] | ||
for shortcut in shortcuts: | ||
trimmed = shortcut["name"].strip() | ||
|
||
if trimmed in names: | ||
print("WARNING: Duplicate shortcut name found: " + shortcut["name"]) | ||
else: | ||
names.append(trimmed) | ||
unique_shortcuts.append(shortcut) | ||
|
||
cmds = [] | ||
|
||
for shortcut in unique_shortcuts: | ||
# simple_print(shortcut) | ||
|
||
# Get icon | ||
icon_path = "" | ||
if shortcut["icon"] != "": | ||
icon_path = shortcut["icon"] | ||
elif shortcut["path"].endswith(".exe"): | ||
icon_path = shortcut["path"] | ||
|
||
dest_path, success = load_icon_from_resource_windows( | ||
icon_path, shortcut["name"] | ||
) | ||
# simple_print(icon_path) | ||
|
||
remote_icon_path = os.path.join("/", "icons", "default_prog_windows.svg") | ||
if success: | ||
remote_icon_path = dest_path.replace( | ||
os.path.join(os.getcwd(), "public"), "" | ||
) | ||
|
||
cmds.append( | ||
Command( | ||
title=f"Run: {shortcut['name']}", | ||
command=lambda path=shortcut["path"]: start_app(path), | ||
description="", | ||
icon=remote_icon_path, | ||
) | ||
) | ||
|
||
return cmds | ||
else: | ||
raise NotImplementedError | ||
|
||
|
||
def load_icon_from_resource_windows(path, fname): | ||
success = False | ||
dest_path = "" | ||
if path.endswith(".exe"): | ||
try: | ||
icon = extract_icon.extract_icon(path, extract_icon.IconSize.LARGE) | ||
print(icon) | ||
|
||
# Store icon in public/icons/programs | ||
dest_path = os.path.join( | ||
os.getcwd(), "public", "icons", "programs", fname + ".ico" | ||
) | ||
icon.save(dest_path) | ||
|
||
print("Saving icon for " + path + " to " + dest_path + ".") | ||
success = True | ||
|
||
except Exception as e: | ||
print(f"Error extracting icon for {path}: {e}") | ||
|
||
elif path.endswith(".ico"): | ||
dest_path = os.path.join( | ||
os.getcwd(), | ||
"public", | ||
"icons", | ||
"programs", | ||
fname + ".ico", | ||
) | ||
|
||
try: | ||
shutil.copy( | ||
path, | ||
dest_path, | ||
) | ||
success = True | ||
except Exception as e: | ||
print(f"Error copying icon for {path}: {e}") | ||
|
||
return dest_path, success | ||
|
||
|
||
def list_shortcuts_windows(directory): | ||
import win32com.client | ||
|
||
out = [] | ||
|
||
""" | ||
Recursively lists all shortcuts in the given directory and writes them to a log file. | ||
""" | ||
if os.path.exists(directory): | ||
for item in os.listdir(directory): | ||
full_path = os.path.join(directory, item) | ||
if os.path.isdir(full_path): | ||
# Recursively search in directories | ||
out = out + list_shortcuts_windows(full_path) | ||
elif item.lower().endswith(".lnk") or item.lower().endswith(".url"): | ||
icon = "" # If empty, means we extract from executable | ||
|
||
if item.lower().endswith(".lnk"): | ||
pywin_client = win32com.client.Dispatch("WScript.Shell") | ||
shortcut = pywin_client.CreateShortCut(full_path) | ||
real_path = shortcut.Targetpath | ||
|
||
elif item.lower().endswith(".url"): | ||
config = configparser.ConfigParser() | ||
config.read(full_path) | ||
|
||
data = dict(config.items("InternetShortcut")) | ||
print(data) | ||
real_path = data["url"] | ||
|
||
if "iconfile" in data: | ||
icon = data["iconfile"] | ||
|
||
name = item[:-4].encode("utf-8", "replace").decode("utf-8") | ||
|
||
out += [{"name": name, "path": real_path, "icon": icon}] | ||
|
||
return out |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
# Attribs: https://pythonassets.com/posts/extract-icon-from-executable-file-windows/ | ||
|
||
from ctypes import Array, byref, c_char, memset, sizeof | ||
from ctypes import c_int, c_void_p, POINTER | ||
from ctypes.wintypes import * | ||
from enum import Enum | ||
import ctypes | ||
|
||
from PIL import Image | ||
|
||
|
||
BI_RGB = 0 | ||
DIB_RGB_COLORS = 0 | ||
|
||
|
||
class ICONINFO(ctypes.Structure): | ||
_fields_ = [ | ||
("fIcon", BOOL), | ||
("xHotspot", DWORD), | ||
("yHotspot", DWORD), | ||
("hbmMask", HBITMAP), | ||
("hbmColor", HBITMAP), | ||
] | ||
|
||
|
||
class RGBQUAD(ctypes.Structure): | ||
_fields_ = [ | ||
("rgbBlue", BYTE), | ||
("rgbGreen", BYTE), | ||
("rgbRed", BYTE), | ||
("rgbReserved", BYTE), | ||
] | ||
|
||
|
||
class BITMAPINFOHEADER(ctypes.Structure): | ||
_fields_ = [ | ||
("biSize", DWORD), | ||
("biWidth", LONG), | ||
("biHeight", LONG), | ||
("biPlanes", WORD), | ||
("biBitCount", WORD), | ||
("biCompression", DWORD), | ||
("biSizeImage", DWORD), | ||
("biXPelsPerMeter", LONG), | ||
("biYPelsPerMeter", LONG), | ||
("biClrUsed", DWORD), | ||
("biClrImportant", DWORD), | ||
] | ||
|
||
|
||
class BITMAPINFO(ctypes.Structure): | ||
_fields_ = [ | ||
("bmiHeader", BITMAPINFOHEADER), | ||
("bmiColors", RGBQUAD * 1), | ||
] | ||
|
||
|
||
shell32 = ctypes.WinDLL("shell32", use_last_error=True) | ||
user32 = ctypes.WinDLL("user32", use_last_error=True) | ||
gdi32 = ctypes.WinDLL("gdi32", use_last_error=True) | ||
|
||
gdi32.CreateCompatibleDC.argtypes = [HDC] | ||
gdi32.CreateCompatibleDC.restype = HDC | ||
gdi32.GetDIBits.argtypes = [HDC, HBITMAP, UINT, UINT, LPVOID, c_void_p, UINT] | ||
gdi32.GetDIBits.restype = c_int | ||
gdi32.DeleteObject.argtypes = [HGDIOBJ] | ||
gdi32.DeleteObject.restype = BOOL | ||
shell32.ExtractIconExW.argtypes = [LPCWSTR, c_int, POINTER(HICON), POINTER(HICON), UINT] | ||
shell32.ExtractIconExW.restype = UINT | ||
user32.GetIconInfo.argtypes = [HICON, POINTER(ICONINFO)] | ||
user32.GetIconInfo.restype = BOOL | ||
user32.DestroyIcon.argtypes = [HICON] | ||
user32.DestroyIcon.restype = BOOL | ||
|
||
|
||
class IconSize(Enum): | ||
SMALL = 1 | ||
LARGE = 2 | ||
|
||
@staticmethod | ||
def to_wh(size: "IconSize") -> tuple[int, int]: | ||
""" | ||
Return the actual (width, height) values for the specified icon size. | ||
""" | ||
size_table = {IconSize.SMALL: (16, 16), IconSize.LARGE: (32, 32)} | ||
return size_table[size] | ||
|
||
|
||
def extract_icon(filename: str, size: IconSize) -> Image: | ||
""" | ||
Extract the icon from the specified `filename`, which might be | ||
either an executable or an `.ico` file. | ||
""" | ||
dc: HDC = gdi32.CreateCompatibleDC(0) | ||
if dc == 0: | ||
raise ctypes.WinError() | ||
|
||
hicon: HICON = HICON() | ||
extracted_icons: UINT = shell32.ExtractIconExW( | ||
filename, | ||
0, | ||
byref(hicon) if size == IconSize.LARGE else None, | ||
byref(hicon) if size == IconSize.SMALL else None, | ||
1, | ||
) | ||
print(extracted_icons) | ||
if extracted_icons != 1: | ||
raise ctypes.WinError() | ||
|
||
def cleanup() -> None: | ||
if icon_info.hbmColor != 0: | ||
gdi32.DeleteObject(icon_info.hbmColor) | ||
if icon_info.hbmMask != 0: | ||
gdi32.DeleteObject(icon_info.hbmMask) | ||
user32.DestroyIcon(hicon) | ||
|
||
icon_info: ICONINFO = ICONINFO(0, 0, 0, 0, 0) | ||
if not user32.GetIconInfo(hicon, byref(icon_info)): | ||
cleanup() | ||
raise ctypes.WinError() | ||
|
||
w, h = IconSize.to_wh(size) | ||
bmi: BITMAPINFO = BITMAPINFO() | ||
memset(byref(bmi), 0, sizeof(bmi)) | ||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER) | ||
bmi.bmiHeader.biWidth = w | ||
bmi.bmiHeader.biHeight = -h | ||
bmi.bmiHeader.biPlanes = 1 | ||
bmi.bmiHeader.biBitCount = 32 | ||
bmi.bmiHeader.biCompression = BI_RGB | ||
bmi.bmiHeader.biSizeImage = w * h * 4 | ||
bits = ctypes.create_string_buffer(bmi.bmiHeader.biSizeImage) | ||
copied_lines = gdi32.GetDIBits( | ||
dc, icon_info.hbmColor, 0, h, bits, byref(bmi), DIB_RGB_COLORS | ||
) | ||
if copied_lines == 0: | ||
cleanup() | ||
raise ctypes.WinError() | ||
|
||
# My code | ||
# Map from BGRA -> RGBA | ||
# Function to swap red and blue bytes in BGRA to get RGBA | ||
def swap_red_blue(bgra_buffer, size): | ||
for i in range(0, size, 4): | ||
# Swap the red and blue bytes (first and third bytes) | ||
bgra_buffer[i], bgra_buffer[i + 2] = bgra_buffer[i + 2], bgra_buffer[i] | ||
|
||
# Call the function to modify the bits buffer | ||
swap_red_blue(bits, bmi.bmiHeader.biSizeImage) | ||
|
||
mode = "RGBA" | ||
image = Image.frombuffer(mode, (w, h), bits, "raw", mode, 0, 1) | ||
|
||
cleanup() | ||
return image |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.