windows notification area icon (WIP) and ketmar postEvent patch

This commit is contained in:
Adam D. Ruppe 2017-03-28 13:51:30 -04:00
parent 7af25bb3f0
commit 5e2eae9438
1 changed files with 112 additions and 10 deletions

View File

@ -1,5 +1,5 @@
/* /*
FIXME: FIXME: WindowTypes.hidden to WindowTypes.eventOnly
Text layout needs a lot of work. Plain drawText is useful but too Text layout needs a lot of work. Plain drawText is useful but too
limited. It will need some kind of text context thing which it will limited. It will need some kind of text context thing which it will
@ -1820,15 +1820,21 @@ public:
/// If `timeoutmsecs` is greater than zero, the event will be delayed for at least `timeoutmsecs` milliseconds. /// If `timeoutmsecs` is greater than zero, the event will be delayed for at least `timeoutmsecs` milliseconds.
bool postTimeout(ET:Object) (ET evt, uint timeoutmsecs) { bool postTimeout(ET:Object) (ET evt, uint timeoutmsecs) {
if (evt is null) return false; // ignore empty events, they can't be handled anyway if (evt is null) return false; // ignore empty events, they can't be handled anyway
if (this.closed) return false; // closed windows can't handle events
// add events even if no event FD/event object created yet // add events even if no event FD/event object created yet
synchronized(this) { synchronized(this) {
if (eventQueueUsed == uint.max) return false; // just in case if (eventQueueUsed == uint.max) return false; // just in case
if (eventQueueUsed < eventQueue.length) { if (eventQueueUsed < eventQueue.length) {
eventQueue[eventQueueUsed++] = QueuedEvent(evt, timeoutmsecs); eventQueue[eventQueueUsed++] = QueuedEvent(evt, timeoutmsecs);
} else { } else {
auto optr = eventQueue.ptr;
eventQueue ~= QueuedEvent(evt, timeoutmsecs); eventQueue ~= QueuedEvent(evt, timeoutmsecs);
++eventQueueUsed; ++eventQueueUsed;
assert(eventQueueUsed == eventQueue.length); assert(eventQueueUsed == eventQueue.length);
if (eventQueue.ptr !is optr) {
import core.memory : GC;
if (eventQueue.ptr is GC.addrOf(eventQueue.ptr)) GC.setAttr(eventQueue.ptr, GC.BlkAttr.NO_INTERIOR);
}
} }
if (!eventWakeUp()) { if (!eventWakeUp()) {
// can't wake up event processor, so there is no reason to keep the event // can't wake up event processor, so there is no reason to keep the event
@ -1848,9 +1854,9 @@ private:
private import core.time : MonoTime; private import core.time : MonoTime;
version(X11) { version(X11) {
int customEventFD = -1; __gshared int customEventFD = -1;
} else version(Windows) { } else version(Windows) {
HANDLE customEventH = null; __gshared HANDLE customEventH = null;
} }
// wake up event processor // wake up event processor
@ -1943,6 +1949,14 @@ private:
} }
} }
// for all windows in nativeMapping
static void processAllCustomEvents () {
foreach (SimpleWindow sw; SimpleWindow.nativeMapping.byValue) {
if (sw is null || sw.closed) continue;
sw.processCustomEvents();
}
}
// 0: infinite (i.e. no scheduled events in queue) // 0: infinite (i.e. no scheduled events in queue)
uint eventQueueTimeoutMSecs () { uint eventQueueTimeoutMSecs () {
synchronized(this) { synchronized(this) {
@ -1962,6 +1976,20 @@ private:
return (res >= int.max ? 0 : res); return (res >= int.max ? 0 : res);
} }
} }
// for all windows in nativeMapping
static uint eventAllQueueTimeoutMSecs () {
uint res = uint.max;
foreach (SimpleWindow sw; SimpleWindow.nativeMapping.byValue) {
if (sw is null || sw.closed) continue;
uint to = sw.eventQueueTimeoutMSecs();
if (to && to < res) {
res = to;
if (to == 1) break; // can't have less than this
}
}
return (res >= int.max ? 0 : res);
}
} }
@ -2840,6 +2868,80 @@ version(Windows) {
} }
} }
///
class NotificationAreaIcon : CapableOfHandlingNativeEvent {
/+
What I actually want from this:
* set / change: icon, tooltip
* handle: mouse click, right click
* show: notification bubble.
+/
WindowsIcon icon;
void delegate(MouseButton button) onClick;
HWND hwnd;
NativeEventHandler getNativeEventHandler() {
return delegate int(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
return 0;
};
}
///
this(string name, MemoryImage icon, void delegate(MouseButton button) onClick) {
this.onClick = onClick;
this.icon = new WindowsIcon(icon);
HINSTANCE hInstance = cast(HINSTANCE) GetModuleHandle(null);
static bool registered = false;
if(!registered) {
WNDCLASSEX wc;
wc.cbSize = wc.sizeof;
wc.hInstance = hInstance;
wc.lpfnWndProc = &WndProc;
wc.lpszClassName = "arsd_simpledisplay_notification_icon"w.ptr;
if(!RegisterClassExW(&wc))
throw new Exception("RegisterClass ");// ~ to!string(GetLastError()));
}
this.hwnd = CreateWindowW("arsd_simpledisplay_notification_icon"w.ptr, "test"w.ptr /* name */, 0 /* dwStyle */, 0, 0, 0, 0, HWND_MESSAGE, null, hInstance, null);
if(hwnd is null)
throw new Exception("CreateWindow");
NOTIFYICONDATA data;
data.cbSize = data.sizeof;
data.hWnd = hwnd;
data.uID = cast(uint) cast(void*) this;
data.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_STATE; // | NIF_SHOWTIP /* use default tooltip, for now. */;
// NIF_INFO means show balloon
data.uCallbackMessage = WM_USER;
data.hIcon = this.icon.hIcon;
data.szTip = ""; // FIXME
//data.dwState = 0; // NIS_HIDDEN; // windows vista
//data.dwStateMask = NIS_HIDDEN; // windows vista
// data.szInfo = ""; // for balloon
// data.szInfoTitle = ""; // for balloon
// data.swInfoFlags = 0; // for balloon https://msdn.microsoft.com/en-us/library/windows/desktop/bb773352(v=vs.85).aspx
// data.hBalloonIcon = null; // for balloon
//data.uVersion = NOTIFYICON_VERSION_4; // Windows Vista and up
Shell_NotifyIcon(NIM_ADD, &data);
}
///
void close () {
}
}
// global hotkey helper function // global hotkey helper function
/// Platform-specific for Windows /// Platform-specific for Windows
@ -5385,17 +5487,17 @@ version(Windows) {
} }
} }
processCustomEvents(); // process events added before event object creation SimpleWindow.processAllCustomEvents(); // process events added before event object creation
while(ret != 0) { while(ret != 0) {
auto wto = eventQueueTimeoutMSecs(); auto wto = SimpleWindow.eventAllQueueTimeoutMSecs();
auto waitResult = MsgWaitForMultipleObjectsEx( auto waitResult = MsgWaitForMultipleObjectsEx(
cast(int) handles.length, handles.ptr, cast(int) handles.length, handles.ptr,
(wto == 0 ? INFINITE : wto), /* timeout */ (wto == 0 ? INFINITE : wto), /* timeout */
0x04FF, /* QS_ALLINPUT */ 0x04FF, /* QS_ALLINPUT */
0x0002 /* MWMO_ALERTABLE */ | 0x0004 /* MWMO_INPUTAVAILABLE */); 0x0002 /* MWMO_ALERTABLE */ | 0x0004 /* MWMO_INPUTAVAILABLE */);
processCustomEvents(); // anyway SimpleWindow.processAllCustomEvents(); // anyway
enum WAIT_OBJECT_0 = 0; enum WAIT_OBJECT_0 = 0;
if(waitResult >= WAIT_OBJECT_0 && waitResult < handles.length + WAIT_OBJECT_0) { if(waitResult >= WAIT_OBJECT_0 && waitResult < handles.length + WAIT_OBJECT_0) {
// process handles[waitResult - WAIT_OBJECT_0]; // process handles[waitResult - WAIT_OBJECT_0];
@ -6716,7 +6818,7 @@ version(X11) {
} }
} }
processCustomEvents(); // process events added before event FD creation SimpleWindow.processAllCustomEvents(); // process events added before event FD creation
{ {
this.mtLock(); this.mtLock();
@ -6725,7 +6827,7 @@ version(X11) {
} }
while(!done) { while(!done) {
bool forceXPending = false; bool forceXPending = false;
auto wto = eventQueueTimeoutMSecs(); auto wto = SimpleWindow.eventAllQueueTimeoutMSecs();
// eh... some events may be queued for "squashing" (or "late delivery"), so we have to do the following magic // eh... some events may be queued for "squashing" (or "late delivery"), so we have to do the following magic
{ {
this.mtLock(); this.mtLock();
@ -6741,7 +6843,7 @@ version(X11) {
throw new Exception("epoll wait failure"); throw new Exception("epoll wait failure");
} }
processCustomEvents(); // anyway SimpleWindow.processAllCustomEvents(); // anyway
//version(sdddd) { import std.stdio; writeln("nfds=", nfds, "; [0]=", events[0].data.fd); } //version(sdddd) { import std.stdio; writeln("nfds=", nfds, "; [0]=", events[0].data.fd); }
foreach(idx; 0 .. nfds) { foreach(idx; 0 .. nfds) {
if(done) break; if(done) break;
@ -6776,7 +6878,7 @@ version(X11) {
ulong n; ulong n;
read(customEventFD, &n, n.sizeof); // reset counter value to zero again read(customEventFD, &n, n.sizeof); // reset counter value to zero again
//{ import core.stdc.stdio; printf("custom event! count=%u\n", eventQueueUsed); } //{ import core.stdc.stdio; printf("custom event! count=%u\n", eventQueueUsed); }
//processCustomEvents(); //SimpleWindow.processAllCustomEvents();
} else { } else {
// some other timer // some other timer
version(sdddd) { import std.stdio; writeln("unknown fd: ", fd); } version(sdddd) { import std.stdio; writeln("unknown fd: ", fd); }