Skip to content

Commit

Permalink
Merge pull request #1616 from contour-terminal/example-cli-color-mode…
Browse files Browse the repository at this point in the history
…-detection

Add example program to detect OS dark/light mode setting in the terminal
  • Loading branch information
christianparpart authored Oct 6, 2024
2 parents 8a32717 + a72a951 commit 7babaa4
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 0 deletions.
5 changes: 5 additions & 0 deletions docs/vt-extensions/color-palette-update-notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ The sent out DSR looks equivalent to the already above mentioned.
This notification is not just sent when dark/light mode has been changed
by the operating system / desktop, but also if the user explicitly changed color scheme,
e.g. by configuration.

## Example source code

Please have a look at our example C++ [source code](https://github.com/contour-terminal/contour/blob/master/examples/detect-dark-light-mode.cpp)
in order to see how to implement this in your own application.
2 changes: 2 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ if(LIBTERMINAL_TESTING)
if(UNIX)
add_executable(watch-mouse-events watch-mouse-events.cpp)
target_link_libraries(watch-mouse-events vtbackend)

add_executable(detect-dark-light-mode detect-dark-light-mode.cpp)
endif()
endif()
133 changes: 133 additions & 0 deletions examples/detect-dark-light-mode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: Apache-2.0
#include <csignal>
#include <cstdlib>
#include <iostream>
#include <string_view>

#include <termios.h>
#include <unistd.h>

using namespace std::literals;

static bool processEvent(std::string_view response)
{
if (response == "\033[?997;1n")
{
std::cout << "dark\n";
return true;
}
else if (response == "\033[?997;2n")
{
std::cout << "light\n";
return true;
}

std::cout << "unknown\n";
return false;
}

static bool queryDarkLightModeOnce()
{
// Also send DA1 to detect end of reply, in case the terminal does not support color mode detection
std::cout << "\033[?996n\033[c";
std::cout.flush();

char buf[32];
size_t n = 0;
size_t i = 0;
while (i < sizeof(buf))
{
if (read(STDIN_FILENO, buf + i, 1) != 1)
break;
else if (buf[i] == 'n')
n = i + 1;
else if (buf[i] == 'c')
{
++i;
break;
}
++i;
}

return processEvent(std::string_view(buf, n));
}

static void signalHandler(int signo)
{
std::signal(signo, SIG_DFL);
std::cerr << "Received signal " << signo << ", exiting...\n";
}

static void monitorDarkLightModeChanges()
{
char buf[32];
size_t n = 0;
size_t i = 0;
while (true)
{
if (i >= sizeof(buf))
i = 0;
if (read(STDIN_FILENO, buf + i, 1) != 1)
break;
else if (buf[i] == '\033')
{
buf[0] = '\033';
i = 1;
}
else if (buf[i] == 'n')
{
n = i + 1;
auto const response = std::string_view(buf, n);
processEvent(response);
}
++i;
}
}

int main(int argc, char* argv[])
{
if (!isatty(STDIN_FILENO))
{
std::cerr << "stdin is not a terminal\n";
return EXIT_FAILURE;
}

termios oldt {};
termios newt {};
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);

if (argc == 2 && (argv[1] == "-h"sv || argv[1] == "--help"sv))
{
std::cout << "Usage: " << argv[0] << " [monitor]\n";
return EXIT_SUCCESS;
}

if (!queryDarkLightModeOnce())
return EXIT_FAILURE;

if (argc == 2 && argv[1] == "monitor"sv)
{
struct sigaction sa;
sa.sa_handler = signalHandler;
sa.sa_flags = 0; // Explicitly don't set SA_RESTART;
sigemptyset(&sa.sa_mask);
for (int const sig: { SIGTERM, SIGINT, SIGQUIT })
sigaction(sig, &sa, nullptr);

std::cerr << "Monitoring dark/light mode changes, press Ctrl+C to exit...\n";
std::cerr << "\033[?2031h"; // Enable dark/light mode change notifications
std::cerr.flush();
monitorDarkLightModeChanges();
std::cerr << "\033[?2031l"; // Disable dark/light mode change notifications
std::cerr << "Finished monitoring dark/light mode changes\n";
std::cerr.flush();
}

// restore terminal
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);

return EXIT_SUCCESS;
}

0 comments on commit 7babaa4

Please sign in to comment.