Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions RimeWithWeasel/RimeWithWeasel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,9 @@ void RimeWithWeaselHandler::SetOption(WeaselSessionId ipc_id,
} else {
rime_api->set_option(to_session_id(ipc_id), opt.c_str(), val);
}
// refresh UI (and tray icon) so the option change takes effect immediately,
// e.g. when toggling ascii_mode from the TSF language bar
_UpdateUI(ipc_id ? ipc_id : m_active_session);
}

void RimeWithWeaselHandler::OnUpdateUI(std::function<void()> const& cb) {
Expand Down
17 changes: 17 additions & 0 deletions WeaselIPCServer/WeaselServerImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,19 @@ LRESULT ServerImpl::OnCommand(UINT uMsg,
return 0;
}

LRESULT ServerImpl::OnServiceNotifyMessage(UINT uMsg,
WPARAM wParam,
LPARAM lParam,
BOOL& bHandled) {
// Runs on the server message thread, NOT on a pipe worker thread and
// without holding g_api_mutex, so that Shell_NotifyIcon inside the tray
// update can never deadlock against the taskbar UI thread.
if (m_trayRefreshCallback) {
m_trayRefreshCallback();
}
return 0;
}

DWORD ServerImpl::OnCommand(WEASEL_IPC_COMMAND uMsg,
DWORD wParam,
DWORD lParam) {
Expand Down Expand Up @@ -466,6 +479,10 @@ void Server::AddMenuHandler(UINT uID, CommandHandler handler) {
m_pImpl->AddMenuHandler(uID, handler);
}

void Server::SetTrayRefreshCallback(std::function<void()> callback) {
m_pImpl->SetTrayRefreshCallback(callback);
}

HWND Server::GetHWnd() {
return m_pImpl->m_hWnd;
}
9 changes: 9 additions & 0 deletions WeaselIPCServer/WeaselServerImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class ServerImpl : public CWindowImpl<ServerImpl, CWindow, ServerWinTraits>
MESSAGE_HANDLER(WM_DWMCOLORIZATIONCOLORCHANGED, OnColorChange)
MESSAGE_HANDLER(WM_SETTINGCHANGE, OnColorChange)
MESSAGE_HANDLER(WM_COMMAND, OnCommand)
MESSAGE_HANDLER(WM_WEASEL_SERVICE_NOTIFY, OnServiceNotifyMessage)
END_MSG_MAP()

LRESULT OnColorChange(UINT uMsg,
Expand All @@ -46,6 +47,10 @@ class ServerImpl : public CWindowImpl<ServerImpl, CWindow, ServerWinTraits>
LPARAM lParam,
BOOL& bHandled);
LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnServiceNotifyMessage(UINT uMsg,
WPARAM wParam,
LPARAM lParam,
BOOL& bHandled);
DWORD OnCommand(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam);
DWORD OnEcho(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam);
DWORD OnStartSession(WEASEL_IPC_COMMAND uMsg, DWORD wParam, DWORD lParam);
Expand Down Expand Up @@ -85,6 +90,9 @@ class ServerImpl : public CWindowImpl<ServerImpl, CWindow, ServerWinTraits>
void AddMenuHandler(UINT uID, CommandHandler& handler) {
m_MenuHandlers[uID] = handler;
}
void SetTrayRefreshCallback(std::function<void()> callback) {
m_trayRefreshCallback = callback;
}

private:
void _Finailize();
Expand All @@ -95,6 +103,7 @@ class ServerImpl : public CWindowImpl<ServerImpl, CWindow, ServerWinTraits>
std::unique_ptr<boost::thread> pipeThread;
RequestHandler* m_pRequestHandler; // reference
std::map<UINT, CommandHandler> m_MenuHandlers;
std::function<void()> m_trayRefreshCallback;
HMODULE m_hUser32Module;
SecurityAttribute sa;
BOOL m_darkMode;
Expand Down
6 changes: 4 additions & 2 deletions WeaselServer/WeaselServerApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ int WeaselServerApp::Run() {
m_ui.Create(m_server.GetHWnd());

m_handler->Initialize();
m_handler->OnUpdateUI([this]() { tray_icon.Refresh(); });
m_handler->OnUpdateUI([this]() { tray_icon.RequestRefresh(); });

tray_icon.Create(m_server.GetHWnd());
tray_icon.Refresh();
m_server.SetTrayRefreshCallback([this]() { tray_icon.ApplyRefresh(); });
tray_icon.RequestRefresh();

int ret = m_server.Run();

tray_icon.DisableRefresh();
m_handler->Finalize();
m_ui.Destroy();
tray_icon.RemoveIcon();
Expand Down
69 changes: 55 additions & 14 deletions WeaselServer/WeaselTrayIcon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,50 @@ BOOL WeaselTrayIcon::Create(HWND hTargetWnd) {
return bRet;
}

void WeaselTrayIcon::Refresh() {
if (!m_style.display_tray_icon &&
!m_status.disabled) // display notification when deploying
void WeaselTrayIcon::RequestRefresh() {
std::lock_guard<std::mutex> lock(m_state_mutex);
if (!m_refresh_enabled) {
return;
}
m_pending_state = WeaselTrayIconState::From(m_style, m_status);
if (m_refresh_pending) {
return;
}
m_refresh_pending = true;
if (!::PostMessage(GetTargetWnd(), WM_WEASEL_SERVICE_NOTIFY, 0, 0)) {
m_refresh_pending = false;
}
}

void WeaselTrayIcon::ApplyRefresh() {
WeaselTrayIconState state;
{
std::lock_guard<std::mutex> lock(m_state_mutex);
if (!m_refresh_pending || !m_refresh_enabled) {
return;
}
state = m_pending_state;
m_refresh_pending = false;
m_refresh_in_progress = true;
}
Refresh(state);
{
std::lock_guard<std::mutex> lock(m_state_mutex);
m_refresh_in_progress = false;
}
m_state_cv.notify_all();
}

void WeaselTrayIcon::DisableRefresh() {
std::unique_lock<std::mutex> lock(m_state_mutex);
m_refresh_enabled = false;
m_refresh_pending = false;
m_state_cv.wait(lock, [this] { return !m_refresh_in_progress; });
}

void WeaselTrayIcon::Refresh(const WeaselTrayIconState& state) {
if (!state.display_tray_icon &&
!state.disabled) // display notification when deploying
{
if (m_mode != INITIAL) {
RemoveIcon();
Expand All @@ -48,24 +89,24 @@ void WeaselTrayIcon::Refresh() {
m_disabled = false;
return;
}
WeaselTrayMode mode = m_status.disabled ? DISABLED
: m_status.ascii_mode ? ASCII
: ZHUNG;
WeaselTrayMode mode = state.disabled ? DISABLED
: state.ascii_mode ? ASCII
: ZHUNG;
/* change icon, when
1,mode changed
2,icon changed
3,both m_schema_zhung_icon and m_style.current_zhung_icon empty(for
initialize) 4,both m_schema_ascii_icon and m_style.current_ascii_icon
3,both m_schema_zhung_icon and state.current_zhung_icon empty(for
initialize) 4,both m_schema_ascii_icon and state.current_ascii_icon
empty(for initialize)
*/
if (mode != m_mode || m_schema_zhung_icon != m_style.current_zhung_icon ||
(m_schema_zhung_icon.empty() && m_style.current_zhung_icon.empty()) ||
m_schema_ascii_icon != m_style.current_ascii_icon ||
(m_schema_ascii_icon.empty() && m_style.current_ascii_icon.empty())) {
if (mode != m_mode || m_schema_zhung_icon != state.current_zhung_icon ||
(m_schema_zhung_icon.empty() && state.current_zhung_icon.empty()) ||
m_schema_ascii_icon != state.current_ascii_icon ||
(m_schema_ascii_icon.empty() && state.current_ascii_icon.empty())) {
ShowIcon();
m_mode = mode;
m_schema_zhung_icon = m_style.current_zhung_icon;
m_schema_ascii_icon = m_style.current_ascii_icon;
m_schema_zhung_icon = state.current_zhung_icon;
m_schema_ascii_icon = state.current_ascii_icon;
if (mode == ASCII) {
if (m_schema_ascii_icon.empty())
SetIcon(mode_icon[mode]);
Expand Down
65 changes: 64 additions & 1 deletion WeaselServer/WeaselTrayIcon.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,54 @@
#include <WeaselIPC.h>
#include "SystemTraySDK.h"

#include <condition_variable>
#include <mutex>

#define WM_WEASEL_TRAY_NOTIFY (WEASEL_IPC_LAST_COMMAND + 100)

// Snapshot of the tray-relevant UI state, computed on the pipe worker thread
// and applied on the server message thread. Keeps Shell_NotifyIcon off the
// pipe worker threads (and away from g_api_mutex), avoiding the deadlock loop
// where the taskbar UI thread waits on the pipe while the server waits for the
// taskbar UI thread inside Shell_NotifyIcon.
struct WeaselTrayIconState {
WeaselTrayIconState()
: valid(false),
display_tray_icon(false),
disabled(false),
ascii_mode(false) {}

static WeaselTrayIconState From(const weasel::UIStyle& style,
const weasel::Status& status) {
WeaselTrayIconState state;
state.valid = true;
state.display_tray_icon = style.display_tray_icon;
state.disabled = status.disabled;
state.ascii_mode = status.ascii_mode;
state.current_zhung_icon = style.current_zhung_icon;
state.current_ascii_icon = style.current_ascii_icon;
return state;
}

bool operator==(const WeaselTrayIconState& rhs) const {
return valid == rhs.valid && display_tray_icon == rhs.display_tray_icon &&
disabled == rhs.disabled && ascii_mode == rhs.ascii_mode &&
current_zhung_icon == rhs.current_zhung_icon &&
current_ascii_icon == rhs.current_ascii_icon;
}

bool operator!=(const WeaselTrayIconState& rhs) const {
return !(*this == rhs);
}

bool valid;
bool display_tray_icon;
bool disabled;
bool ascii_mode;
std::wstring current_zhung_icon;
std::wstring current_ascii_icon;
};

class WeaselTrayIcon : public CSystemTray {
public:
enum WeaselTrayMode {
Expand All @@ -17,15 +63,32 @@ class WeaselTrayIcon : public CSystemTray {
WeaselTrayIcon(weasel::UI& ui);

BOOL Create(HWND hTargetWnd);
void Refresh();

// Captures the tray-relevant state and posts a refresh request to the server
// message thread. Never calls Shell_NotifyIcon itself.
void RequestRefresh();
void DisableRefresh();

// Runs on the server message thread (no g_api_mutex held).
void ApplyRefresh();

protected:
virtual void CustomizeMenu(HMENU hMenu);

void Refresh(const WeaselTrayIconState& state);

weasel::UIStyle& m_style;
weasel::Status& m_status;
WeaselTrayMode m_mode;
std::wstring m_schema_zhung_icon;
std::wstring m_schema_ascii_icon;
bool m_disabled;

// Guarded by m_state_mutex.
bool m_refresh_enabled = true;
bool m_refresh_pending = false;
bool m_refresh_in_progress = false;
WeaselTrayIconState m_pending_state;
std::mutex m_state_mutex;
std::condition_variable m_state_cv;
};
8 changes: 8 additions & 0 deletions include/WeaselIPC.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ enum WEASEL_IPC_COMMAND {
WEASEL_IPC_LAST_COMMAND
};

// Posted by WeaselTrayIcon to the server window so that Shell_NotifyIcon runs
// on the server message thread instead of a pipe worker thread.
#define WM_WEASEL_SERVICE_NOTIFY (WEASEL_IPC_LAST_COMMAND + 200)

namespace weasel {
struct PipeMessage {
WEASEL_IPC_COMMAND Msg;
Expand Down Expand Up @@ -163,6 +167,10 @@ class Server {
void AddMenuHandler(UINT uID, CommandHandler handler);
HWND GetHWnd();

// Callback invoked on the server message thread when a tray icon refresh is
// requested from a pipe worker thread.
void SetTrayRefreshCallback(std::function<void()> callback);

private:
ServerImpl* m_pImpl;
};
Expand Down
Loading