Skip to content

Commit

Permalink
Add ability to start the application minimised to tray by specifying …
Browse files Browse the repository at this point in the history
…`--tray` launch option.

Addresses Issue #1.
  • Loading branch information
mini)(ant committed Sep 17, 2018
1 parent 42c054b commit 1ca396b
Show file tree
Hide file tree
Showing 14 changed files with 427 additions and 11 deletions.
16 changes: 14 additions & 2 deletions real-app/real-app.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@
<ClCompile Include="src\CurlWrapper\Writers\CurlMemoryWriter.cpp" />
<ClCompile Include="src\CurlWrapper\Writers\CurlWriter.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\OStreamSink.cpp" />
<ClCompile Include="src\Windows\Console.cpp" />
<ClCompile Include="src\Windows\Exception.cpp" />
<ClCompile Include="src\Windows\MessagingWindow.cpp" />
<ClCompile Include="src\Windows\MinimumLatencyAudioClient.cpp" />
<ClCompile Include="src\Version.cpp" />
<ClCompile Include="src\Windows\Filesystem.cpp" />
<ClCompile Include="src\Windows\GlobalWindowProcedure.cpp" />
<ClCompile Include="src\Windows\TrayIcon.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
Expand All @@ -30,9 +36,15 @@
<ClInclude Include="src\CurlWrapper\Writers\CurlFileWriter.h" />
<ClInclude Include="src\CurlWrapper\Writers\CurlWriter.h" />
<ClInclude Include="src\CurlWrapper\Writers\CurlMemoryWriter.h" />
<ClInclude Include="src\OStreamSink.h" />
<ClInclude Include="src\Windows\Console.h" />
<ClInclude Include="src\Windows\Exception.h" />
<ClInclude Include="src\Windows\MessagingWindow.h" />
<ClInclude Include="src\Windows\MinimumLatencyAudioClient.h" />
<ClInclude Include="src\Version.h" />
<ClInclude Include="src\Windows\Filesystem.h" />
<ClInclude Include="src\Windows\GlobalWindowProcedure.h" />
<ClInclude Include="src\Windows\TrayIcon.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="real-app.rc" />
Expand Down Expand Up @@ -101,7 +113,7 @@
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>NotSet</SubSystem>
<SubSystem>Windows</SubSystem>
<AdditionalDependencies>libcurl_a.lib;Ws2_32.lib;crypt32.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
Expand All @@ -123,7 +135,7 @@
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<SubSystem>NotSet</SubSystem>
<SubSystem>Windows</SubSystem>
<AdditionalDependencies>libcurl_a.lib;Ws2_32.lib;crypt32.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
Expand Down
23 changes: 23 additions & 0 deletions real-app/src/OStreamSink.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "OStreamSink.h"

using namespace miniant::Spdlog;

OStreamSink::OStreamSink(std::shared_ptr<std::ostream> outputStream, bool forceFlush):
m_outputStream(std::move(outputStream)),
m_forceFlush(forceFlush) {}

std::mutex& OStreamSink::GetMutex() {
return mutex_;
}

void OStreamSink::sink_it_(const spdlog::details::log_msg& message) {
fmt::memory_buffer formattedMessage;
sink::formatter_->format(message, formattedMessage);
m_outputStream->write(formattedMessage.data(), static_cast<std::streamsize>(formattedMessage.size()));
if (m_forceFlush)
m_outputStream->flush();
}

void OStreamSink::flush_() {
m_outputStream->flush();
}
27 changes: 27 additions & 0 deletions real-app/src/OStreamSink.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

// Work-around for compilation error caused by spdlog using Windows headers
#include <Windows.h>

#include <spdlog/sinks/base_sink.h>

#include <mutex>

namespace miniant::Spdlog {

class OStreamSink : public spdlog::sinks::base_sink<std::mutex> {
public:
explicit OStreamSink(std::shared_ptr<std::ostream> outputStream, bool forceFlush=false);

std::mutex& GetMutex();

protected:
void sink_it_(const spdlog::details::log_msg& message) override;
void flush_() override;

private:
std::shared_ptr<std::ostream> m_outputStream;
bool m_forceFlush;
};

}
26 changes: 26 additions & 0 deletions real-app/src/Windows/Console.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "Console.h"

#include <Windows.h>

#include <iostream>

using namespace miniant::Windows;

Console::Console(std::function<void()> onShow):
m_onShow(std::move(onShow)) {}

void Console::Open() {
::AllocConsole();

FILE* dummy;
freopen_s(&dummy, "conout$", "w", stdout);
std::cout.clear();

if (m_onShow)
m_onShow();
}

void Console::Close() {
::SendMessage(::GetConsoleWindow(), WM_CLOSE, 0, 0);
::FreeConsole();
}
18 changes: 18 additions & 0 deletions real-app/src/Windows/Console.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <functional>

namespace miniant::Windows {

class Console {
public:
Console(std::function<void()> onShow);

void Open();
void Close();

private:
std::function<void()> m_onShow;
};

}
14 changes: 14 additions & 0 deletions real-app/src/Windows/Exception.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include "Exception.h"

#include <Windows.h>

#include <string>

using namespace miniant::Windows;

std::string GetErrorMessage() noexcept {
DWORD lastError = ::GetLastError();
return "Last error: " + std::to_string(lastError);
}

Exception::Exception() noexcept: std::runtime_error(GetErrorMessage()) {}
12 changes: 12 additions & 0 deletions real-app/src/Windows/Exception.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <stdexcept>

namespace miniant::Windows {

class Exception : std::runtime_error {
public:
Exception() noexcept;
};

}
46 changes: 46 additions & 0 deletions real-app/src/Windows/GlobalWindowProcedure.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "GlobalWindowProcedure.h"

#include "Exception.h"

using namespace miniant::Windows;

std::unordered_map<HWND, GlobalWindowProcedure::WindowProcedure> GlobalWindowProcedure::s_windowProcedureMap;

UINT GlobalWindowProcedure::GetFreeEventId() noexcept {
static UINT nextFreeEventId = WM_USER;
return nextFreeEventId++;
}

WNDCLASS GlobalWindowProcedure::RegisterWindowClass(LPCTSTR lpszClassName) {
WNDCLASS wc = {};
wc.lpszClassName = lpszClassName;
wc.lpfnWndProc = &WndProc;
if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<LPCWSTR>(&WndProc), &wc.hInstance))
throw Exception();

if (::RegisterClass(&wc) == 0)
throw Exception();

return wc;
}

void GlobalWindowProcedure::SetWindowProcedure(HWND hWnd, WindowProcedure procedure) {
s_windowProcedureMap[hWnd] = procedure;
}

LRESULT CALLBACK GlobalWindowProcedure::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
WindowProcedure* windowProcedure;
try {
windowProcedure = &s_windowProcedureMap.at(hWnd);
} catch (std::out_of_range&) {
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}

if (*windowProcedure) {
std::optional<LRESULT> lResult = (*windowProcedure)(hWnd, uMsg, wParam, lParam);
if (lResult)
return lResult.value();
}

return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
26 changes: 26 additions & 0 deletions real-app/src/Windows/GlobalWindowProcedure.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include <Windows.h>

#include <functional>
#include <map>
#include <optional>

namespace miniant::Windows {

class GlobalWindowProcedure {
public:
using WindowProcedure = std::function<std::optional<LRESULT>(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lparam)>;

static UINT GetFreeEventId() noexcept;

static WNDCLASS RegisterWindowClass(LPCTSTR lpszClassName);
static void SetWindowProcedure(HWND hWnd, WindowProcedure procedure);

private:
static std::unordered_map<HWND, WindowProcedure> s_windowProcedureMap;

static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};

}
45 changes: 45 additions & 0 deletions real-app/src/Windows/MessagingWindow.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "MessagingWindow.h"

#include "Exception.h"
#include "GlobalWindowProcedure.h"

using namespace miniant::Windows;

constexpr TCHAR CLASS_NAME[] = TEXT("MessagingWindow");

MessagingWindow::MessagingWindow() {
m_hWnd = ::CreateWindowEx(
0,
CLASS_NAME,
NULL,
0,
0, 0, 0, 0,
HWND_MESSAGE,
NULL,
GlobalWindowProcedure::RegisterWindowClass(CLASS_NAME).hInstance,
NULL);
if (m_hWnd == NULL)
throw Exception();

GlobalWindowProcedure::SetWindowProcedure(m_hWnd, [this](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
auto eventHandler = m_eventHandlerMap.find(uMsg);
if (eventHandler != m_eventHandlerMap.end()) {
if (eventHandler->second)
return eventHandler->second(*this, wParam, lParam);
}

return std::optional<LRESULT>();
});
}

MessagingWindow::~MessagingWindow() noexcept {
GlobalWindowProcedure::SetWindowProcedure(m_hWnd, nullptr);
}

HWND MessagingWindow::GetHWindow() noexcept {
return m_hWnd;
}

void MessagingWindow::SetEventHandler(UINT event, EventHandler handler) {
m_eventHandlerMap[event] = std::move(handler);
}
27 changes: 27 additions & 0 deletions real-app/src/Windows/MessagingWindow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <Windows.h>

#include <functional>
#include <map>
#include <optional>

namespace miniant::Windows {

class MessagingWindow {
public:
using EventHandler = std::function<std::optional<LRESULT>(MessagingWindow&, WPARAM, LPARAM)>;

MessagingWindow();
~MessagingWindow() noexcept;

HWND GetHWindow() noexcept;

void SetEventHandler(UINT event, EventHandler handler);

private:
HWND m_hWnd;
std::unordered_map<UINT, EventHandler> m_eventHandlerMap;
};

}
56 changes: 56 additions & 0 deletions real-app/src/Windows/TrayIcon.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include "TrayIcon.h"

#include "Exception.h"
#include "GlobalWindowProcedure.h"

using namespace miniant::Windows;

TrayIcon::TrayIcon(MessagingWindow& window, HICON hIcon):
m_window(window) {
m_data = {};
m_data.cbSize = sizeof(m_data);
m_data.uVersion = NOTIFYICON_VERSION_4;
m_data.uFlags = NIF_ICON | NIF_MESSAGE;
m_data.hWnd = window.GetHWindow();
m_data.hIcon = hIcon;
m_data.uID = 1;
m_data.uCallbackMessage = GlobalWindowProcedure::GetFreeEventId();

window.SetEventHandler(m_data.uCallbackMessage, [this](const MessagingWindow& window, WPARAM wParam, LPARAM lParam) {
switch (LOWORD(lParam)) {
case WM_LBUTTONUP:
if (m_lButtonUpHandler)
m_lButtonUpHandler(*this);
break;

default:
break;
}

return std::optional<LRESULT>();
});
}

TrayIcon::~TrayIcon() noexcept {
m_window.SetEventHandler(m_data.uCallbackMessage, nullptr);
::Shell_NotifyIcon(NIM_DELETE, &m_data);
}

void TrayIcon::Show() {
if (!::Shell_NotifyIcon(NIM_ADD, &m_data))
throw Exception();

if (!::Shell_NotifyIcon(NIM_SETVERSION, &m_data)) {
::Shell_NotifyIcon(NIM_DELETE, &m_data);
throw Exception();
}
}

void TrayIcon::Hide() {
if (!::Shell_NotifyIcon(NIM_DELETE, &m_data))
throw Exception();
}

void TrayIcon::SetLButtonUpHandler(TrayEventHandler handler) noexcept {
m_lButtonUpHandler = std::move(handler);
}
Loading

0 comments on commit 1ca396b

Please sign in to comment.