basic working as tray
This commit is contained in:
parent
b6ffab3ae3
commit
70163d4e12
|
@ -46,6 +46,7 @@ pub fn build(b: *std.build.Builder) void {
|
||||||
// woah...we don't actually need libc!
|
// woah...we don't actually need libc!
|
||||||
exe.linkSystemLibrary("user32");
|
exe.linkSystemLibrary("user32");
|
||||||
exe.linkSystemLibrary("kernel32");
|
exe.linkSystemLibrary("kernel32");
|
||||||
|
exe.linkSystemLibrary("shell32");
|
||||||
}
|
}
|
||||||
exe.install();
|
exe.install();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
const w = std.os.windows;
|
const w = std.os.windows;
|
||||||
|
|
||||||
extern "user32" fn MessageBoxA(hWnd: ?w.HANDLE, lpText: ?w.LPCSTR, lpCaption: ?w.LPCSTR, uType: w.UINT) callconv(w.WINAPI) c_int;
|
extern "user32" fn MessageBoxA(hWnd: ?w.HANDLE, lpText: ?w.LPCSTR, lpCaption: ?w.LPCSTR, uType: w.UINT) callconv(w.WINAPI) c_int;
|
||||||
|
@ -341,6 +342,10 @@ const WM_DRAWCLIPBOARD = @as(u32, 776);
|
||||||
const WM_SIZECLIPBOARD = @as(u32, 779);
|
const WM_SIZECLIPBOARD = @as(u32, 779);
|
||||||
const WM_CHANGECBCHAIN = @as(u32, 781);
|
const WM_CHANGECBCHAIN = @as(u32, 781);
|
||||||
|
|
||||||
|
const WM_USER = @as(u32, 1024);
|
||||||
|
// USER constants here - not part of Windows headers
|
||||||
|
const WM_TRAY = @as(u32, WM_USER + 1);
|
||||||
|
|
||||||
const RECT = extern struct {
|
const RECT = extern struct {
|
||||||
left: i32,
|
left: i32,
|
||||||
top: i32,
|
top: i32,
|
||||||
|
@ -616,6 +621,199 @@ extern "KERNEL32" fn GlobalFree(
|
||||||
hMem: isize,
|
hMem: isize,
|
||||||
) callconv(w.WINAPI) isize;
|
) callconv(w.WINAPI) isize;
|
||||||
|
|
||||||
|
// Shell
|
||||||
|
const Arch = enum { X86, X64, Arm64 };
|
||||||
|
const arch: Arch = switch (builtin.target.cpu.arch) {
|
||||||
|
.i386 => .X86,
|
||||||
|
.x86_64 => .X64,
|
||||||
|
.arm, .armeb => .Arm64,
|
||||||
|
else => @compileError("unable to determine win32 arch"),
|
||||||
|
};
|
||||||
|
const NOTIFY_ICON_DATA_FLAGS = enum(u32) {
|
||||||
|
MESSAGE = 1,
|
||||||
|
ICON = 2,
|
||||||
|
TIP = 4,
|
||||||
|
STATE = 8,
|
||||||
|
INFO = 16,
|
||||||
|
GUID = 32,
|
||||||
|
REALTIME = 64,
|
||||||
|
SHOWTIP = 128,
|
||||||
|
_,
|
||||||
|
pub fn initFlags(o: struct {
|
||||||
|
MESSAGE: u1 = 0,
|
||||||
|
ICON: u1 = 0,
|
||||||
|
TIP: u1 = 0,
|
||||||
|
STATE: u1 = 0,
|
||||||
|
INFO: u1 = 0,
|
||||||
|
GUID: u1 = 0,
|
||||||
|
REALTIME: u1 = 0,
|
||||||
|
SHOWTIP: u1 = 0,
|
||||||
|
}) NOTIFY_ICON_DATA_FLAGS {
|
||||||
|
return @intToEnum(NOTIFY_ICON_DATA_FLAGS, (if (o.MESSAGE == 1) @enumToInt(NOTIFY_ICON_DATA_FLAGS.MESSAGE) else 0) | (if (o.ICON == 1) @enumToInt(NOTIFY_ICON_DATA_FLAGS.ICON) else 0) | (if (o.TIP == 1) @enumToInt(NOTIFY_ICON_DATA_FLAGS.TIP) else 0) | (if (o.STATE == 1) @enumToInt(NOTIFY_ICON_DATA_FLAGS.STATE) else 0) | (if (o.INFO == 1) @enumToInt(NOTIFY_ICON_DATA_FLAGS.INFO) else 0) | (if (o.GUID == 1) @enumToInt(NOTIFY_ICON_DATA_FLAGS.GUID) else 0) | (if (o.REALTIME == 1) @enumToInt(NOTIFY_ICON_DATA_FLAGS.REALTIME) else 0) | (if (o.SHOWTIP == 1) @enumToInt(NOTIFY_ICON_DATA_FLAGS.SHOWTIP) else 0));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const NIF_MESSAGE = NOTIFY_ICON_DATA_FLAGS.MESSAGE;
|
||||||
|
const NIF_ICON = NOTIFY_ICON_DATA_FLAGS.ICON;
|
||||||
|
const NIF_TIP = NOTIFY_ICON_DATA_FLAGS.TIP;
|
||||||
|
const NIF_STATE = NOTIFY_ICON_DATA_FLAGS.STATE;
|
||||||
|
const NIF_INFO = NOTIFY_ICON_DATA_FLAGS.INFO;
|
||||||
|
const NIF_GUID = NOTIFY_ICON_DATA_FLAGS.GUID;
|
||||||
|
const NIF_REALTIME = NOTIFY_ICON_DATA_FLAGS.REALTIME;
|
||||||
|
const NIF_SHOWTIP = NOTIFY_ICON_DATA_FLAGS.SHOWTIP;
|
||||||
|
const NOTIFYICONDATAA = switch (arch) {
|
||||||
|
.X64, .Arm64 => extern struct {
|
||||||
|
cbSize: u32,
|
||||||
|
hWnd: ?w.HWND,
|
||||||
|
uID: u32,
|
||||||
|
uFlags: NOTIFY_ICON_DATA_FLAGS,
|
||||||
|
uCallbackMessage: u32,
|
||||||
|
hIcon: ?w.HICON,
|
||||||
|
szTip: [128]w.CHAR,
|
||||||
|
dwState: u32,
|
||||||
|
dwStateMask: u32,
|
||||||
|
szInfo: [256]w.CHAR,
|
||||||
|
Anonymous: extern union {
|
||||||
|
uTimeout: u32,
|
||||||
|
uVersion: u32,
|
||||||
|
},
|
||||||
|
szInfoTitle: [64]w.CHAR,
|
||||||
|
dwInfoFlags: u32,
|
||||||
|
guidItem: Guid,
|
||||||
|
hBalloonIcon: ?w.HICON,
|
||||||
|
},
|
||||||
|
.X86 => packed struct {
|
||||||
|
cbSize: u32,
|
||||||
|
hWnd: ?w.HWND,
|
||||||
|
uID: u32,
|
||||||
|
uFlags: NOTIFY_ICON_DATA_FLAGS,
|
||||||
|
uCallbackMessage: u32,
|
||||||
|
hIcon: ?w.HICON,
|
||||||
|
szTip: [128]w.CHAR,
|
||||||
|
dwState: u32,
|
||||||
|
dwStateMask: u32,
|
||||||
|
szInfo: [256]w.CHAR,
|
||||||
|
Anonymous: packed union {
|
||||||
|
uTimeout: u32,
|
||||||
|
uVersion: u32,
|
||||||
|
},
|
||||||
|
szInfoTitle: [64]w.CHAR,
|
||||||
|
dwInfoFlags: u32,
|
||||||
|
guidItem: Guid,
|
||||||
|
hBalloonIcon: ?w.HICON,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const NOTIFY_ICON_MESSAGE = enum(u32) {
|
||||||
|
ADD = 0,
|
||||||
|
MODIFY = 1,
|
||||||
|
DELETE = 2,
|
||||||
|
SETFOCUS = 3,
|
||||||
|
SETVERSION = 4,
|
||||||
|
};
|
||||||
|
const NIM_ADD = NOTIFY_ICON_MESSAGE.ADD;
|
||||||
|
const NIM_MODIFY = NOTIFY_ICON_MESSAGE.MODIFY;
|
||||||
|
const NIM_DELETE = NOTIFY_ICON_MESSAGE.DELETE;
|
||||||
|
const NIM_SETFOCUS = NOTIFY_ICON_MESSAGE.SETFOCUS;
|
||||||
|
const NIM_SETVERSION = NOTIFY_ICON_MESSAGE.SETVERSION;
|
||||||
|
|
||||||
|
const WM_LBUTTONDOWN = @as(u32, 513);
|
||||||
|
const WM_LBUTTONUP = @as(u32, 514);
|
||||||
|
const WM_LBUTTONDBLCLK = @as(u32, 515);
|
||||||
|
|
||||||
|
extern "SHELL32" fn Shell_NotifyIconA(
|
||||||
|
dwMessage: NOTIFY_ICON_MESSAGE,
|
||||||
|
lpData: ?*NOTIFYICONDATAA,
|
||||||
|
) callconv(w.WINAPI) BOOL;
|
||||||
|
|
||||||
|
pub fn typedConst(comptime T: type, comptime value: anytype) T {
|
||||||
|
return typedConst2(T, T, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn typedConst2(comptime ReturnType: type, comptime SwitchType: type, comptime value: anytype) ReturnType {
|
||||||
|
const target_type_error = @as([]const u8, "typedConst cannot convert to " ++ @typeName(ReturnType));
|
||||||
|
const value_type_error = @as([]const u8, "typedConst cannot convert " ++ @typeName(@TypeOf(value)) ++ " to " ++ @typeName(ReturnType));
|
||||||
|
|
||||||
|
switch (@typeInfo(SwitchType)) {
|
||||||
|
.Int => |target_type_info| {
|
||||||
|
if (value >= std.math.maxInt(SwitchType)) {
|
||||||
|
if (target_type_info.signedness == .signed) {
|
||||||
|
const UnsignedT = @Type(std.builtin.TypeInfo{ .Int = .{ .signedness = .unsigned, .bits = target_type_info.bits } });
|
||||||
|
return @bitCast(SwitchType, @as(UnsignedT, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
.Pointer => |target_type_info| switch (target_type_info.size) {
|
||||||
|
.One, .Many, .C => {
|
||||||
|
switch (@typeInfo(@TypeOf(value))) {
|
||||||
|
.ComptimeInt, .Int => {
|
||||||
|
const usize_value = if (value >= 0) value else @bitCast(usize, @as(isize, value));
|
||||||
|
return @intToPtr(ReturnType, usize_value);
|
||||||
|
},
|
||||||
|
else => @compileError(value_type_error),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => target_type_error,
|
||||||
|
},
|
||||||
|
.Optional => |target_type_info| switch (@typeInfo(target_type_info.child)) {
|
||||||
|
.Pointer => return typedConst2(ReturnType, target_type_info.child, value),
|
||||||
|
else => target_type_error,
|
||||||
|
},
|
||||||
|
.Enum => |_| switch (@typeInfo(@TypeOf(value))) {
|
||||||
|
.Int => return @intToEnum(ReturnType, value),
|
||||||
|
else => target_type_error,
|
||||||
|
},
|
||||||
|
else => @compileError(target_type_error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Guid = extern union {
|
||||||
|
Ints: extern struct {
|
||||||
|
a: u32,
|
||||||
|
b: u16,
|
||||||
|
c: u16,
|
||||||
|
d: [8]u8,
|
||||||
|
},
|
||||||
|
Bytes: [16]u8,
|
||||||
|
|
||||||
|
const big_endian_hex_offsets = [16]u6{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34 };
|
||||||
|
const little_endian_hex_offsets = [16]u6{ 6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34 };
|
||||||
|
const hex_offsets = switch (builtin.target.cpu.arch.endian()) {
|
||||||
|
.Big => big_endian_hex_offsets,
|
||||||
|
.Little => little_endian_hex_offsets,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn initString(s: []const u8) Guid {
|
||||||
|
var guid = Guid{ .Bytes = undefined };
|
||||||
|
for (hex_offsets) |hex_offset, i| {
|
||||||
|
//guid.Bytes[i] = decodeHexByte(s[offset..offset+2]);
|
||||||
|
guid.Bytes[i] = decodeHexByte([2]u8{ s[hex_offset], s[hex_offset + 1] });
|
||||||
|
}
|
||||||
|
return guid;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
comptime {
|
||||||
|
std.debug.assert(@sizeOf(Guid) == 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: is this in the standard lib somewhere?
|
||||||
|
fn hexVal(c: u8) u4 {
|
||||||
|
if (c <= '9') return @intCast(u4, c - '0');
|
||||||
|
if (c >= 'a') return @intCast(u4, c + 10 - 'a');
|
||||||
|
return @intCast(u4, c + 10 - 'A');
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: is this in the standard lib somewhere?
|
||||||
|
fn decodeHexByte(hex: [2]u8) u8 {
|
||||||
|
return @intCast(u8, hexVal(hex[0])) << 4 | hexVal(hex[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const IDI_APPLICATION = typedConst([*:0]const u16, @as(u32, 32512));
|
||||||
|
|
||||||
|
pub extern "USER32" fn LoadIconW(
|
||||||
|
hInstance: ?w.HINSTANCE,
|
||||||
|
lpIconName: ?[*:0]const u16,
|
||||||
|
) callconv(w.WINAPI) ?w.HICON;
|
||||||
|
|
||||||
// resource.h
|
// resource.h
|
||||||
// #define IDD_MFPLAYBACK_DIALOG 102
|
// #define IDD_MFPLAYBACK_DIALOG 102
|
||||||
// #define IDM_EXIT 105
|
// #define IDM_EXIT 105
|
||||||
|
@ -627,10 +825,22 @@ extern "KERNEL32" fn GlobalFree(
|
||||||
// #define IDC_STATIC -1
|
// #define IDC_STATIC -1
|
||||||
const IDM_EXIT = @as(u32, 105);
|
const IDM_EXIT = @as(u32, 105);
|
||||||
|
|
||||||
|
var h_instance: w.HINSTANCE = undefined;
|
||||||
|
|
||||||
|
fn getWinStyleString(comptime buflen: u32, str: []const u8) [buflen]u8 {
|
||||||
|
var buf: [buflen]u8 = .{0} ** buflen;
|
||||||
|
for (str) |c, i| buf[i] = c;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
pub export fn wWinMain(hInstance: w.HINSTANCE, hPrevInstance: ?w.HINSTANCE, lpCmdLine: w.PWSTR, nCmdShow: w.INT) w.INT {
|
pub export fn wWinMain(hInstance: w.HINSTANCE, hPrevInstance: ?w.HINSTANCE, lpCmdLine: w.PWSTR, nCmdShow: w.INT) w.INT {
|
||||||
_ = hPrevInstance;
|
_ = hPrevInstance;
|
||||||
_ = lpCmdLine;
|
_ = lpCmdLine;
|
||||||
|
_ = nCmdShow;
|
||||||
|
|
||||||
|
h_instance = hInstance;
|
||||||
|
|
||||||
|
dbg_msg = getWinStyleString(17, "TODO: Send now: x");
|
||||||
// Register the window class.
|
// Register the window class.
|
||||||
var wc: WNDCLASSA = .{
|
var wc: WNDCLASSA = .{
|
||||||
.lpszClassName = "Clipboard watcher",
|
.lpszClassName = "Clipboard watcher",
|
||||||
|
@ -662,7 +872,7 @@ pub export fn wWinMain(hInstance: w.HINSTANCE, hPrevInstance: ?w.HINSTANCE, lpCm
|
||||||
CW_USEDEFAULT,
|
CW_USEDEFAULT,
|
||||||
CW_USEDEFAULT,
|
CW_USEDEFAULT,
|
||||||
|
|
||||||
null, // Parent window
|
null, // Parent window: use HWND_MESSAGE to make this completely invisible
|
||||||
null, // Menu
|
null, // Menu
|
||||||
hInstance, // Instance handle
|
hInstance, // Instance handle
|
||||||
null, // Additional application data
|
null, // Additional application data
|
||||||
|
@ -671,8 +881,6 @@ pub export fn wWinMain(hInstance: w.HINSTANCE, hPrevInstance: ?w.HINSTANCE, lpCm
|
||||||
if (hwnd == null)
|
if (hwnd == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
_ = ShowWindow(hwnd, @intToEnum(SHOW_WINDOW_CMD, nCmdShow));
|
|
||||||
|
|
||||||
// Run the message loop.
|
// Run the message loop.
|
||||||
|
|
||||||
var msg: MSG = .{
|
var msg: MSG = .{
|
||||||
|
@ -699,6 +907,14 @@ pub export fn wWinMain(hInstance: w.HINSTANCE, hPrevInstance: ?w.HINSTANCE, lpCm
|
||||||
var uFormat: w.UINT = @bitReverse(c_uint, 0);
|
var uFormat: w.UINT = @bitReverse(c_uint, 0);
|
||||||
var fAuto: w.BOOL = w.TRUE;
|
var fAuto: w.BOOL = w.TRUE;
|
||||||
var hwndNextViewer: ?w.HWND = null;
|
var hwndNextViewer: ?w.HWND = null;
|
||||||
|
var icon_data: NOTIFYICONDATAA = undefined;
|
||||||
|
var dbg_msg: [17]u8 = undefined;
|
||||||
|
var cnt: u8 = 0;
|
||||||
|
var window_state: union(enum(u8)) {
|
||||||
|
INITIAL = 0,
|
||||||
|
NORMAL = 1,
|
||||||
|
MINIMIZED = 2,
|
||||||
|
} = .INITIAL;
|
||||||
|
|
||||||
fn MainWndProc(hwnd: w.HWND, uMsg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w.WINAPI) w.LRESULT { //APIENTRY {
|
fn MainWndProc(hwnd: w.HWND, uMsg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w.WINAPI) w.LRESULT { //APIENTRY {
|
||||||
// static HWND hwndNextViewer;
|
// static HWND hwndNextViewer;
|
||||||
|
@ -779,6 +995,10 @@ fn MainWndProc(hwnd: w.HWND, uMsg: u32, wParam: w.WPARAM, lParam: w.LPARAM) call
|
||||||
var lpstr = GlobalLock(hnd).?;
|
var lpstr = GlobalLock(hnd).?;
|
||||||
|
|
||||||
_ = GetClientRect(hwnd, &rc);
|
_ = GetClientRect(hwnd, &rc);
|
||||||
|
// TODO: copy our lpstr and ship it to our common handler
|
||||||
|
cnt = (cnt + 1) % 10;
|
||||||
|
dbg_msg[dbg_msg.len - 1] = '0' + cnt;
|
||||||
|
_ = MessageBoxA(hwnd, @ptrCast([*:0]const u8, &dbg_msg), "Debug", 0);
|
||||||
_ = DrawTextA(hdc, @ptrCast([*:0]const u8, lpstr), -1, &rc, DT_LEFT);
|
_ = DrawTextA(hdc, @ptrCast([*:0]const u8, lpstr), -1, &rc, DT_LEFT);
|
||||||
|
|
||||||
_ = GlobalUnlock(hnd);
|
_ = GlobalUnlock(hnd);
|
||||||
|
@ -839,8 +1059,57 @@ fn MainWndProc(hwnd: w.HWND, uMsg: u32, wParam: w.WPARAM, lParam: w.LPARAM) call
|
||||||
// Add the window to the clipboard viewer chain.
|
// Add the window to the clipboard viewer chain.
|
||||||
|
|
||||||
hwndNextViewer = SetClipboardViewer(hwnd);
|
hwndNextViewer = SetClipboardViewer(hwnd);
|
||||||
|
var tip = getWinStyleString(128, "Clipboard processor");
|
||||||
|
icon_data = .{
|
||||||
|
.cbSize = @sizeOf(@TypeOf(icon_data)),
|
||||||
|
.hWnd = hwnd,
|
||||||
|
.uFlags = NOTIFY_ICON_DATA_FLAGS.initFlags(.{
|
||||||
|
.MESSAGE = 1,
|
||||||
|
.ICON = 1,
|
||||||
|
.TIP = 1,
|
||||||
|
}),
|
||||||
|
.uCallbackMessage = WM_TRAY,
|
||||||
|
.uID = 4242,
|
||||||
|
.hIcon = LoadIconW(null, IDI_APPLICATION), // TODO: Custom icon
|
||||||
|
.szTip = tip,
|
||||||
|
.dwState = 0,
|
||||||
|
.dwStateMask = 0,
|
||||||
|
.szInfo = getWinStyleString(256, "Info"),
|
||||||
|
.Anonymous = .{ .uTimeout = 0 },
|
||||||
|
.szInfoTitle = getWinStyleString(64, "Info Title"),
|
||||||
|
.dwInfoFlags = 0,
|
||||||
|
.guidItem = Guid.initString("3e781b84-3ffd-44a1-b3ab-11d0f90136f9"), // generated from duckduckgo
|
||||||
|
.hBalloonIcon = null,
|
||||||
|
};
|
||||||
|
// stData.hIcon = g_hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_TRAYICON));
|
||||||
|
// LoadStringSafe(IDS_TIP, stData.szTip, _countof(stData.szTip));
|
||||||
|
if (Shell_NotifyIconA(NIM_ADD, &icon_data) != w.TRUE) {
|
||||||
|
_ = MessageBoxA(hwnd, "Notification Icon failed to create", "Error", 0);
|
||||||
|
return -1; // oops
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
WM_TRAY => {
|
||||||
|
switch (lParam) {
|
||||||
|
WM_LBUTTONUP => {
|
||||||
|
switch (window_state) {
|
||||||
|
.INITIAL => {
|
||||||
|
_ = ShowWindow(hwnd, SHOW_WINDOW_CMD.SHOWNORMAL);
|
||||||
|
window_state = .NORMAL;
|
||||||
|
},
|
||||||
|
.NORMAL => {
|
||||||
|
_ = ShowWindow(hwnd, SHOW_WINDOW_CMD.HIDE);
|
||||||
|
window_state = .MINIMIZED;
|
||||||
|
},
|
||||||
|
.MINIMIZED => {
|
||||||
|
_ = ShowWindow(hwnd, SHOW_WINDOW_CMD.SHOW);
|
||||||
|
window_state = .NORMAL;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
WM_CHANGECBCHAIN => {
|
WM_CHANGECBCHAIN => {
|
||||||
|
|
||||||
// If the next window is closing, repair the chain.
|
// If the next window is closing, repair the chain.
|
||||||
|
@ -858,6 +1127,7 @@ fn MainWndProc(hwnd: w.HWND, uMsg: u32, wParam: w.WPARAM, lParam: w.LPARAM) call
|
||||||
|
|
||||||
WM_DESTROY => {
|
WM_DESTROY => {
|
||||||
_ = ChangeClipboardChain(hwnd, hwndNextViewer);
|
_ = ChangeClipboardChain(hwnd, hwndNextViewer);
|
||||||
|
_ = Shell_NotifyIconA(NIM_DELETE, &icon_data);
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user