-
-
Notifications
You must be signed in to change notification settings - Fork 124
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
new Win32pai: * Shell_NotifyIcon * GetSubMenu * DestroyMenu * TrackPopupMenuEx new struct: * NOTIFYICONDATA * TPMPARAMS new constants: * Shell_NotifyIcon uFlags * Shell_NotifyIcon WndProc callback msg * NOTIFYICONDATA::uFlags * NOTIFYICONDATA::dwState * NOTIFYICONDATA::dwInfoFlags * TrackPopupMenuEx uFlag * LoadImage type, fuLoad
- Loading branch information
Showing
12 changed files
with
969 additions
and
5 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 |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import 'dart:ffi'; | ||
import 'dart:io'; | ||
|
||
import 'package:win32/win32.dart'; | ||
|
||
final hInst = GetModuleHandle(nullptr); | ||
|
||
const EVENT_QUIT = WM_APP + 2; | ||
const EVENT_TRAY_NOTIFY = WM_APP + 1; | ||
|
||
typedef LocalWndProc = bool Function(int hWnd, | ||
int uMsg, int wParam, int lParam); | ||
|
||
final wndProc = Pointer.fromFunction<WindowProc>(_appWndProc, 0); | ||
|
||
void exec() { | ||
final msg = MSG.allocate().addressOf; | ||
while (GetMessage(msg, NULL, 0, 0) != 0) { | ||
TranslateMessage(msg); | ||
DispatchMessage(msg); | ||
} | ||
} | ||
|
||
int loadDartIcon() { | ||
final dartIconPath = _thisPath('dart.ico'); | ||
return LoadImage( | ||
0, | ||
TEXT(dartIconPath), | ||
IMAGE_ICON, | ||
0, | ||
0, | ||
LR_LOADFROMFILE | LR_DEFAULTSIZE | LR_SHARED | ||
); | ||
} | ||
|
||
final _localWndProcs = <LocalWndProc>[]; | ||
|
||
/// Use in iterateLocalWndProcs | ||
void registryWdnProc(LocalWndProc proc) => _localWndProcs.add(proc); | ||
|
||
void deregisterWndProc(LocalWndProc proc) { | ||
_localWndProcs.remove(proc); | ||
} | ||
int _appWndProc(int hWnd, int uMsg, int wParam, int lParam) { | ||
if (iterateLocalWndProcs(hWnd, uMsg, wParam, lParam)) { | ||
return TRUE; | ||
} | ||
|
||
switch(uMsg) { | ||
case WM_CLOSE: | ||
ShowWindow(hWnd, SW_HIDE); | ||
return TRUE; | ||
} | ||
return DefWindowProc(hWnd, uMsg, wParam, lParam); | ||
} | ||
|
||
bool iterateLocalWndProcs(int hWnd, int uMsg, int wParam, int lParam) { | ||
for(final proc in _localWndProcs) { | ||
final isProcProcessed = proc(hWnd, uMsg, wParam, lParam); | ||
if (isProcProcessed) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
String _thisPath(String fileName) => Platform.script | ||
.toFilePath() | ||
.replaceFirst(RegExp(r'[^\\]+$'), fileName); |
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,63 @@ | ||
import 'dart:ffi'; | ||
import 'dart:math'; | ||
|
||
import 'package:ffi/ffi.dart'; | ||
import 'package:win32/win32.dart'; | ||
|
||
import '_app.dart' as app; | ||
import '_tray.dart' as tray; | ||
|
||
void show({required int hWndParent}) { | ||
final mousePos = _currentMousePos(); | ||
final hMenu = _buildMenu(); | ||
|
||
SetForegroundWindow(hWndParent); | ||
TrackPopupMenuEx(hMenu, | ||
_contextMenuFlags, | ||
mousePos.x, mousePos.y, | ||
hWndParent, nullptr); | ||
|
||
DestroyMenu(hMenu); | ||
} | ||
|
||
bool wndProc(int hWnd, int uMsg, int wParam, int lParam) { | ||
switch (uMsg) { | ||
case WM_COMMAND: | ||
final param = LOWORD(wParam); | ||
switch (param) { | ||
case app.EVENT_QUIT: | ||
tray.removeIcon(); | ||
PostQuitMessage(0); | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
int _buildMenu() { | ||
final hMenu = CreateMenu(); | ||
AppendMenu(hMenu, MF_STRING, app.EVENT_QUIT, TEXT("&Quit")); | ||
|
||
final hMenubar = CreateMenu(); | ||
AppendMenu(hMenubar, MF_POPUP, hMenu, TEXT("_Parent")); | ||
|
||
return GetSubMenu(hMenubar, 0); | ||
} | ||
|
||
Point<int> _currentMousePos() { | ||
final point = POINT.allocate(); | ||
GetCursorPos(point.addressOf); | ||
final result = Point(point.x, point.y); | ||
free(point.addressOf); | ||
return result; | ||
} | ||
|
||
int get _contextMenuFlags { | ||
var uFlags = TPM_RIGHTBUTTON; | ||
if (GetSystemMetrics(SM_MENUDROPALIGNMENT) != 0) { | ||
uFlags |= TPM_RIGHTALIGN; | ||
} else { | ||
uFlags |= TPM_LEFTALIGN; | ||
} | ||
return uFlags; | ||
} |
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,65 @@ | ||
import 'dart:ffi'; | ||
import 'package:ffi/ffi.dart'; | ||
import 'package:win32/win32.dart'; | ||
|
||
import '_app.dart' as app; | ||
import '_menu.dart' as menu; | ||
|
||
NOTIFYICONDATA _nid = NOTIFYICONDATA.allocate(); | ||
|
||
bool _trayWndProc(int hWnd, int msg, int wParam, int lParam) { | ||
switch (msg) { | ||
case app.EVENT_TRAY_NOTIFY: | ||
final trayMsg = _fixNotifyDataToVersion4(LOWORD(lParam)); | ||
switch (trayMsg) { | ||
case NIN_SELECT: | ||
ShowWindow(_nid.hWnd, IsWindowVisible(_nid.hWnd) == 1 | ||
? SW_HIDE | ||
: SW_SHOW); | ||
SetForegroundWindow(_nid.hWnd); | ||
return true; | ||
|
||
case WM_CONTEXTMENU: | ||
menu.show(hWndParent: hWnd); | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
void addIcon({required int hWndParent}) { | ||
_nid.hWnd = hWndParent; | ||
_nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP | NIF_GUID; | ||
_nid.szTip = 'Dart tray'; | ||
_nid.uCallbackMessage = app.EVENT_TRAY_NOTIFY; | ||
_nid.hIcon = app.loadDartIcon(); | ||
|
||
Shell_NotifyIcon(NIM_ADD, _nid.addressOf); | ||
|
||
// TODO: uVersion does not yet support. See NOTIFYICONDATA declaration | ||
// nid.uVersion = 4; | ||
// Shell_NotifyIcon(NIM_SETVERSION, nid.addressOf); | ||
|
||
app.registryWdnProc(_trayWndProc); | ||
} | ||
|
||
void removeIcon() { | ||
Shell_NotifyIcon(NIM_DELETE, _nid.addressOf); | ||
free(_nid.addressOf); | ||
app.deregisterWndProc(_trayWndProc); | ||
} | ||
|
||
int _fixNotifyDataToVersion4(int msg) { | ||
switch(msg) { | ||
case 521: return WM_MBUTTONDBLCLK; | ||
case 520: return WM_MBUTTONUP; | ||
case 519: return WM_MBUTTONDOWN; | ||
case 517: return WM_CONTEXTMENU; | ||
case 516: return WM_RBUTTONDOWN; | ||
case 515: return WM_LBUTTONDBLCLK; | ||
case 514: return NIN_SELECT; | ||
case 513: return WM_LBUTTONDOWN; | ||
case 512: return WM_MOUSEMOVE; | ||
} | ||
return msg; | ||
} |
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,60 @@ | ||
import 'dart:ffi'; | ||
import 'dart:math' as math; | ||
|
||
import 'package:win32/win32.dart'; | ||
|
||
import '_app.dart' as app; | ||
|
||
bool _windowWndProc(int hWnd, int uMsg, int wParam, int lParam) { | ||
switch(uMsg) { | ||
case WM_CLOSE: | ||
ShowWindow(hWnd, SW_HIDE); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
int createHidden() { | ||
final windowClassNme = _regWinClass(); | ||
final rect = _getWindowCenterRect(); | ||
final hWnd = CreateWindowEx( | ||
0, | ||
TEXT(windowClassNme), | ||
TEXT('Tray Callback Window'), | ||
WS_OVERLAPPEDWINDOW, | ||
rect.left, | ||
rect.top, | ||
rect.width, | ||
rect.height, | ||
NULL, | ||
NULL, | ||
app.hInst, | ||
nullptr); | ||
app.registryWdnProc(_windowWndProc); | ||
return hWnd; | ||
} | ||
|
||
String _regWinClass() { | ||
const windowClass = 'Tray_Callback_Window'; | ||
final wc = WNDCLASS.allocate(); | ||
wc.style = CS_HREDRAW | CS_VREDRAW; | ||
wc.lpfnWndProc = app.wndProc; | ||
wc.hInstance = app.hInst; | ||
wc.hIcon = app.loadDartIcon(); | ||
wc.hCursor = LoadCursor(NULL, IDC_ARROW); | ||
wc.lpszClassName = TEXT(windowClass); | ||
RegisterClass(wc.addressOf); | ||
return windowClass; | ||
} | ||
|
||
math.Rectangle<int> _getWindowCenterRect() { | ||
const windowWidth = 500; | ||
const windowHeight = 250; | ||
|
||
final screenWidth = GetSystemMetrics(SM_CXFULLSCREEN); | ||
final screenHeight = GetSystemMetrics(SM_CYFULLSCREEN); | ||
|
||
final x = (screenWidth - windowWidth) ~/ 2; | ||
final y = (screenHeight - windowHeight) ~/ 2; | ||
return math.Rectangle(x, y, windowWidth, windowHeight); | ||
} |
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,12 @@ | ||
import '_app.dart' as app; | ||
import '_menu.dart' as menu; | ||
import '_tray.dart' as tray; | ||
import '_window.dart' as window; | ||
|
||
void main() { | ||
final hWnd = window.createHidden(); | ||
tray.addIcon(hWndParent: hWnd); | ||
app.registryWdnProc(menu.wndProc); | ||
app.exec(); | ||
} | ||
|
Oops, something went wrong.