Add active window title widget

This widget called "Title" is a simple text with the title of the
currently active window. To prevent clipping the size of the title is capped by the
config variable "MaxTitleLength".

Implements https://github.com/scorpion-26/gBar/issues/77
This commit is contained in:
scorpion-26 2024-03-17 15:46:45 +01:00
parent 3a7fd719a2
commit 70594dae44
11 changed files with 147 additions and 3 deletions

View file

@ -20,6 +20,10 @@
font-size: 16px;
}
.title-text {
font-size: 16px;
}
.reboot-button {
font-size: 28px;
color: #6272a4;

View file

@ -45,6 +45,10 @@ $textsize: 16px;
font-size: $textsize;
}
.title-text {
font-size: $textsize;
}
.reboot-button {
font-size: 28px;

View file

@ -119,6 +119,10 @@ DateTimeStyle: %a %D - %H:%M:%S %Z
# Set datetime locale (defaults to system locale if not set or set to empty string)
#DateTimeLocale: de_DE.utf8
# How many characters of the title can be displayed. Note that higher values *will* cause styling issues, especially when it is in the center.
# If you have the title in the center, consider also increasing "CenterSpace"
MaxTitleLength: 30
# Adds a audio input(aka. microphone) widget
AudioInput: false

View file

@ -23,6 +23,15 @@ ext_workspace_header = custom_target('generate-ext-workspace-header',
output: ['ext-workspace-unstable-v1.h'],
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'])
wlr_foreign_toplevel_src = custom_target('generate-wlr-foreign-toplevel-src',
input: ['protocols/wlr-foreign-toplevel-management-unstable-v1.xml'],
output: ['wlr-foreign-toplevel-management-unstable-v1.c'],
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'])
wlr_foreign_toplevel_header = custom_target('generate-wlr-foreign-toplevel-header',
input: ['protocols/wlr-foreign-toplevel-management-unstable-v1.xml'],
output: ['wlr-foreign-toplevel-management-unstable-v1.h'],
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'])
gtk = dependency('gtk+-3.0')
gtk_layer_shell = dependency('gtk-layer-shell-0')
@ -46,6 +55,8 @@ headers = [
sources = [
ext_workspace_src,
ext_workspace_header,
wlr_foreign_toplevel_src,
wlr_foreign_toplevel_header,
'src/Window.cpp',
'src/Widget.cpp',
'src/Wayland.cpp',

View file

@ -4,8 +4,8 @@
#include "Common.h"
#include "Config.h"
#include "SNI.h"
#include <cmath>
#include <mutex>
#include <cstdlib>
namespace Bar
{
@ -389,6 +389,20 @@ namespace Bar
return TimerResult::Ok;
}
TimerResult UpdateTitle(Text& text)
{
std::string title = System::GetActiveWindowTitle();
if (title.size() > Config::Get().maxTitleLength)
{
constexpr std::string_view ellipsis = "...";
uint32_t newLength = std::max(Config::Get().maxTitleLength - ellipsis.size(), (size_t)0);
title.resize(newLength);
title += ellipsis;
}
text.SetText(title);
return TimerResult::Ok;
}
#ifdef WITH_WORKSPACES
static std::vector<Button*> workspaces;
TimerResult UpdateWorkspaces(Box&)
@ -1085,6 +1099,18 @@ namespace Bar
parent.AddChild(std::move(time));
}
void WidgetTitle(Widget& parent, Side side)
{
auto title = Widget::Create<Text>();
Utils::SetTransform(*title, {-1, side == Side::Center, SideToAlignment(side)});
title->SetAngle(Utils::GetAngle());
title->SetClass("widget");
title->AddClass("title-text");
title->SetText("Uninitialized");
title->AddTimer<Text>(DynCtx::UpdateTitle, DynCtx::updateTimeFast);
parent.AddChild(std::move(title));
}
void ChooseWidgetToDraw(const std::string& widgetName, Widget& parent, Side side)
{
if (widgetName == "Workspaces")
@ -1102,6 +1128,11 @@ namespace Bar
WidgetTime(parent, side);
return;
}
if (widgetName == "Title")
{
WidgetTitle(parent, side);
return;
}
if (widgetName == "Tray")
{
#ifdef WITH_SNI
@ -1184,7 +1215,7 @@ namespace Bar
}
LOG("Warning: Unkwown widget name " << widgetName << "!"
<< "\n\tKnown names are: Workspaces, Time, Tray, Packages, Audio, Bluetooth, Network, Sensors, Disk, "
"VRAM, GPU, RAM, CPU, Battery, Power");
"VRAM, GPU, RAM, CPU, Battery, Power, Title");
}
void Create(Window& window, const std::string& monitorName)

View file

@ -302,6 +302,7 @@ void Config::Load(const std::string& overrideConfigLocation)
AddConfigVar("CheckUpdateInterval", config.checkUpdateInterval, lineView, foundProperty);
AddConfigVar("CenterSpace", config.centerSpace, lineView, foundProperty);
AddConfigVar("MaxTitleLength", config.maxTitleLength, lineView, foundProperty);
AddConfigVar("NumWorkspaces", config.numWorkspaces, lineView, foundProperty);
AddConfigVar("AudioScrollSpeed", config.audioScrollSpeed, lineView, foundProperty);
AddConfigVar("SensorSize", config.sensorSize, lineView, foundProperty);

View file

@ -75,6 +75,7 @@ public:
uint32_t audioScrollSpeed = 5; // 5% each scroll
uint32_t checkUpdateInterval = 5 * 60; // Interval to run the "checkPackagesCommand". In seconds
uint32_t centerSpace = 300; // How much space should be reserved for the center widgets.
uint32_t maxTitleLength = 30; // Maximum chars of the title widget. Longer titles will be shortened
uint32_t numWorkspaces = 9; // How many workspaces to display
uint32_t sensorSize = 24; // The size of the circular sensors
uint32_t networkIconSize = 24; // The size of the two network arrows

View file

@ -647,6 +647,16 @@ namespace System
return str.str();
}
std::string GetActiveWindowTitle()
{
Wayland::PollEvents();
const Wayland::Window* activeWindow = Wayland::GetActiveWindow();
if (!activeWindow)
return "No Active Window"; // TODO Customize!!
return activeWindow->title;
}
void Shutdown()
{
system("shutdown 0");

View file

@ -113,6 +113,8 @@ namespace System
std::string GetTime();
std::string GetActiveWindowTitle();
void Shutdown();
void Reboot();
void ExitWM();

View file

@ -2,8 +2,10 @@
#include "Common.h"
#include "Config.h"
#include <wayland-client-protocol.h>
#include <wayland-client.h>
#include <ext-workspace-unstable-v1.h>
#include <wlr-foreign-toplevel-management-unstable-v1.h>
namespace Wayland
{
@ -11,10 +13,12 @@ namespace Wayland
static std::unordered_map<wl_output*, Monitor> monitors;
static std::unordered_map<zext_workspace_group_handle_v1*, WorkspaceGroup> workspaceGroups;
static std::unordered_map<zext_workspace_handle_v1*, Workspace> workspaces;
static std::unordered_map<zwlr_foreign_toplevel_handle_v1*, Window> windows;
static wl_display* display;
static wl_registry* registry;
static zext_workspace_manager_v1* workspaceManager;
static zwlr_foreign_toplevel_manager_v1* toplevelManager;
static bool registeredMonitor = false;
static bool registeredGroup = false;
@ -127,6 +131,52 @@ namespace Wayland
}
zext_workspace_manager_v1_listener workspaceManagerListener = {OnWSManagerNewGroup, OnWSManagerDone, OnWSManagerFinished};
// zwlr_foreign_toplevel_handle_v1
static void OnTLTitle(void*, zwlr_foreign_toplevel_handle_v1* toplevel, const char* title)
{
auto window = windows.find(toplevel);
ASSERT(window != windows.end(), "Wayland: OnTLTile called on unknwon toplevel!");
window->second.title = title;
}
static void OnTLOutputEnter(void*, UNUSED zwlr_foreign_toplevel_handle_v1* toplevel, UNUSED wl_output* output) {}
static void OnTLOutputLeave(void*, UNUSED zwlr_foreign_toplevel_handle_v1* toplevel, UNUSED wl_output* output) {}
static void OnTLState(void*, zwlr_foreign_toplevel_handle_v1* toplevel, wl_array* state)
{
auto window = windows.find(toplevel);
ASSERT(window != windows.end(), "Wayland: OnTLState called on unknwon toplevel!");
// Unrolled from wl_array_for_each, but with types defined to compile under C++
// There doesn't seem to be any documentation on the element size of the state, so use wlr's internally used uint32_t
bool activated = false;
for (uint32_t* curPtr = (uint32_t*)state->data; (uint8_t*)curPtr < (uint8_t*)state->data + state->size; curPtr++)
{
switch (*curPtr)
{
case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: activated = true; break;
default: break;
}
}
window->second.activated = activated;
}
static void OnTLAppID(void*, zwlr_foreign_toplevel_handle_v1* toplevel, const char* appId) {}
static void OnTLDone(void*, zwlr_foreign_toplevel_handle_v1*) {}
static void OnTLClosed(void*, zwlr_foreign_toplevel_handle_v1* toplevel)
{
windows.erase(toplevel);
zwlr_foreign_toplevel_handle_v1_destroy(toplevel);
}
static void OnTLParent(void*, zwlr_foreign_toplevel_handle_v1*, zwlr_foreign_toplevel_handle_v1*) {}
zwlr_foreign_toplevel_handle_v1_listener toplevelListener = {OnTLTitle, OnTLAppID, OnTLOutputEnter, OnTLOutputLeave,
OnTLState, OnTLDone, OnTLClosed, OnTLParent};
// zwlr_foreign_toplevel_manager_v1
static void OnToplevel(void*, zwlr_foreign_toplevel_manager_v1*, zwlr_foreign_toplevel_handle_v1* toplevel)
{
windows.emplace(toplevel, Window{});
zwlr_foreign_toplevel_handle_v1_add_listener(toplevel, &toplevelListener, nullptr);
}
static void OnTLManagerFinished(void*, zwlr_foreign_toplevel_manager_v1*) {}
zwlr_foreign_toplevel_manager_v1_listener toplevelManagerListener = {OnToplevel, OnTLManagerFinished};
// Output Callbacks
// Very bloated, indeed
static void OnOutputGeometry(void*, wl_output*, int32_t, int32_t, int32_t, int32_t, int32_t, const char*, const char*, int32_t) {}
@ -162,11 +212,17 @@ namespace Wayland
LOG("Wayland: Register <pending> at ID " << mon.ID);
wl_output_add_listener(output, &outputListener, nullptr);
}
if (strcmp(interface, "zext_workspace_manager_v1") == 0 && !Config::Get().useHyprlandIPC)
else if (strcmp(interface, "zext_workspace_manager_v1") == 0 && !Config::Get().useHyprlandIPC)
{
workspaceManager = (zext_workspace_manager_v1*)wl_registry_bind(registry, name, &zext_workspace_manager_v1_interface, version);
zext_workspace_manager_v1_add_listener(workspaceManager, &workspaceManagerListener, nullptr);
}
else if (strcmp(interface, "zwlr_foreign_toplevel_manager_v1") == 0)
{
toplevelManager = (zwlr_foreign_toplevel_manager_v1*)wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface,
version);
zwlr_foreign_toplevel_manager_v1_add_listener(toplevelManager, &toplevelManagerListener, nullptr);
}
}
static void OnRegistryRemove(void*, wl_registry*, uint32_t name)
{
@ -305,6 +361,18 @@ namespace Wayland
return it->second.ID;
}
const Window* GetActiveWindow()
{
auto it = std::find_if(windows.begin(), windows.end(),
[&](const std::pair<zwlr_foreign_toplevel_handle_v1*, Window>& el)
{
return el.second.activated == true;
});
if (it == windows.end())
return nullptr;
return &it->second;
}
const std::unordered_map<wl_output*, Monitor>& GetMonitors()
{
return monitors;

View file

@ -29,6 +29,12 @@ namespace Wayland
zext_workspace_handle_v1* lastActiveWorkspace;
};
struct Window
{
std::string title;
bool activated;
};
void Init();
void PollEvents();
@ -61,5 +67,7 @@ namespace Wayland
});
}
const Window* GetActiveWindow();
void Shutdown();
}