Improve handling of output names

Now can use panel --output-name list to list available outputs.
Also, --output-name works on macOS
This commit is contained in:
Kovid Goyal 2025-05-13 15:29:37 +05:30
parent 67c1ce7280
commit 88f4c829eb
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
15 changed files with 100 additions and 24 deletions

View file

@ -1945,6 +1945,17 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
return &window->ns.layer_shell.config;
}
static NSScreen*
screen_for_name(const char *name) {
int count = 0;
GLFWmonitor **monitors = glfwGetMonitors(&count);
for (int i = 0; i < count; i++) {
const char *q = glfwGetMonitorName(monitors[i]);
if (q && strcmp(q, name) == 0) return ((_GLFWmonitor*)monitors[i])->ns.screen;
}
return NULL;
}
bool
_glfwPlatformSetLayerShellConfig(_GLFWwindow* window, const GLFWLayerShellConfig *value) {
#define config window->ns.layer_shell.config
@ -1975,6 +1986,10 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
// HACK: Changing the style mask can cause the first responder to be cleared
[nswindow makeFirstResponder:window->ns.view];
NSScreen *screen = screen_for_window_center(window);
if (config.output_name[0]) {
NSScreen *q = screen_for_name(config.output_name);
if (q) screen = q;
}
unsigned cell_width, cell_height; double left_edge_spacing, top_edge_spacing, right_edge_spacing, bottom_edge_spacing;
float xscale = (float)config.expected.xscale, yscale = (float)config.expected.yscale;
_glfwPlatformGetWindowContentScale(window, &xscale, &yscale);

3
glfw/glfw3.h vendored
View file

@ -1314,7 +1314,7 @@ typedef struct GLFWLayerShellConfig {
int requested_top_margin, requested_left_margin, requested_bottom_margin, requested_right_margin;
} previous;
bool was_toggled_to_fullscreen;
char output_name[64];
char output_name[128];
GLFWFocusPolicy focus_policy;
unsigned x_size_in_cells, x_size_in_pixels;
unsigned y_size_in_cells, y_size_in_pixels;
@ -2385,6 +2385,7 @@ GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, flo
* @ingroup monitor
*/
GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor);
GLFWAPI const char* glfwGetMonitorDescription(GLFWmonitor* monitor);
/*! @brief Sets the user pointer of the specified monitor.
*

2
glfw/internal.h vendored
View file

@ -490,7 +490,7 @@ struct _GLFWwindow
//
struct _GLFWmonitor
{
char* name;
const char *name, *description;
void* userPointer;
// Physical dimensions in millimeters.

15
glfw/monitor.c vendored
View file

@ -186,7 +186,8 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor)
_glfwFreeGammaArrays(&monitor->currentRamp);
free(monitor->modes);
free(monitor->name);
free((void*)monitor->name);
free((void*)monitor->description);
free(monitor);
}
@ -393,9 +394,19 @@ GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle)
assert(monitor != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return monitor->name;
return monitor->name ? monitor->name : "";
}
GLFWAPI const char* glfwGetMonitorDescription(GLFWmonitor* handle)
{
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
assert(monitor != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return monitor->description ? monitor->description : "";
}
GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer)
{
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;

24
glfw/wl_monitor.c vendored
View file

@ -41,20 +41,15 @@ static void outputHandleGeometry(void* data,
int32_t physicalWidth,
int32_t physicalHeight,
int32_t subpixel UNUSED,
const char* make,
const char* model,
const char* make UNUSED,
const char* model UNUSED,
int32_t transform UNUSED)
{
struct _GLFWmonitor *monitor = data;
char name[1024];
monitor->wl.x = x;
monitor->wl.y = y;
monitor->widthMM = physicalWidth;
monitor->heightMM = physicalHeight;
snprintf(name, sizeof(name), "%s %s", make, model);
monitor->name = _glfw_strdup(name);
}
static void outputHandleMode(void* data,
@ -105,15 +100,20 @@ static void outputHandleName(void* data,
struct wl_output* output UNUSED,
const char* name) {
struct _GLFWmonitor *monitor = data;
if (name) strncpy(monitor->wl.friendly_name, name, sizeof(monitor->wl.friendly_name)-1);
if (name) {
if (monitor->name) free((void*)monitor->name);
monitor->name = _glfw_strdup(name);
}
}
static void outputHandleDescription(void* data,
struct wl_output* output UNUSED,
const char* description) {
struct _GLFWmonitor *monitor = data;
if (description) strncpy(monitor->wl.description, description, sizeof(monitor->wl.description)-1);
if (description) {
if (monitor->description) free((void*)monitor->description);
monitor->description = _glfw_strdup(description);
}
}
static const struct wl_output_listener outputListener = {
@ -142,8 +142,8 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version)
return;
}
// The actual name of this output will be set in the geometry handler.
monitor = _glfwAllocMonitor(NULL, 0, 0);
// The actual name of this output will be set in the handlers.
monitor = _glfwAllocMonitor("unnamed", 0, 0);
output = wl_registry_bind(_glfw.wl.registry,
name,

1
glfw/wl_platform.h vendored
View file

@ -406,7 +406,6 @@ typedef struct _GLFWmonitorWayland
{
struct wl_output* output;
uint32_t name;
char friendly_name[64], description[64];
int currentMode;
int x;

2
glfw/wl_window.c vendored
View file

@ -971,7 +971,7 @@ find_output_by_name(const char* name) {
if (!name || !name[0]) return NULL;
for (int i = 0; i < _glfw.monitorCount; i++) {
_GLFWmonitor *m = _glfw.monitors[i];
if (strcmp(m->wl.friendly_name, name) == 0) return m->wl.output;
if (strcmp(m->name, name) == 0) return m->wl.output;
}
return NULL;
}

View file

@ -66,9 +66,9 @@ func main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {
argv = append(argv, fmt.Sprintf("--override=background_opacity=%f", conf.Background_opacity))
if runtime.GOOS != "darwin" {
argv = append(argv, fmt.Sprintf("--app-id=%s", conf.App_id))
if conf.Output_name != "" {
argv = append(argv, fmt.Sprintf("--output-name=%s", conf.Output_name))
}
}
if conf.Output_name != "" {
argv = append(argv, fmt.Sprintf("--output-name=%s", conf.Output_name))
}
argv = append(argv, fmt.Sprintf("--focus-policy=%s", conf.Focus_policy))
if conf.Start_as_hidden {

View file

@ -71,9 +71,10 @@
opt('output_name', '', long_text='''
On Wayland or X11, the panel can only be displayed on a single monitor (output) at a time. This allows
The panel can only be displayed on a single monitor (output) at a time. This allows
you to specify which output is used, by name. If not specified the compositor will choose an
output automatically, typically the last output the user interacted with or the primary monitor.
You can get a list of available outputs by running: :code:`kitten panel --output-name list`.
''')
opt('start_as_hidden', 'no', option_type='to_bool',

View file

@ -1570,6 +1570,7 @@ def set_os_window_pos(os_window_id: int, x: int, y: int) -> None:
def get_all_processes() -> Tuple[int, ...]:
pass
def get_monitor_names() -> tuple[tuple[str, str], ...]: ...
def num_users() -> int:
pass

3
kitty/glfw-wrapper.c generated
View file

@ -89,6 +89,9 @@ load_glfw(const char* path) {
*(void **) (&glfwGetMonitorName_impl) = dlsym(handle, "glfwGetMonitorName");
if (glfwGetMonitorName_impl == NULL) fail("Failed to load glfw function glfwGetMonitorName with error: %s", dlerror());
*(void **) (&glfwGetMonitorDescription_impl) = dlsym(handle, "glfwGetMonitorDescription");
if (glfwGetMonitorDescription_impl == NULL) fail("Failed to load glfw function glfwGetMonitorDescription with error: %s", dlerror());
*(void **) (&glfwSetMonitorUserPointer_impl) = dlsym(handle, "glfwSetMonitorUserPointer");
if (glfwSetMonitorUserPointer_impl == NULL) fail("Failed to load glfw function glfwSetMonitorUserPointer with error: %s", dlerror());

6
kitty/glfw-wrapper.h generated
View file

@ -1052,7 +1052,7 @@ typedef struct GLFWLayerShellConfig {
int requested_top_margin, requested_left_margin, requested_bottom_margin, requested_right_margin;
} previous;
bool was_toggled_to_fullscreen;
char output_name[64];
char output_name[128];
GLFWFocusPolicy focus_policy;
unsigned x_size_in_cells, x_size_in_pixels;
unsigned y_size_in_cells, y_size_in_pixels;
@ -1817,6 +1817,10 @@ typedef const char* (*glfwGetMonitorName_func)(GLFWmonitor*);
GFW_EXTERN glfwGetMonitorName_func glfwGetMonitorName_impl;
#define glfwGetMonitorName glfwGetMonitorName_impl
typedef const char* (*glfwGetMonitorDescription_func)(GLFWmonitor*);
GFW_EXTERN glfwGetMonitorDescription_func glfwGetMonitorDescription_impl;
#define glfwGetMonitorDescription glfwGetMonitorDescription_impl
typedef void (*glfwSetMonitorUserPointer_func)(GLFWmonitor*, void*);
GFW_EXTERN glfwSetMonitorUserPointer_func glfwSetMonitorUserPointer_impl;
#define glfwSetMonitorUserPointer glfwSetMonitorUserPointer_impl

View file

@ -2143,6 +2143,23 @@ get_monitor_workarea(PYNOARG) {
return Py_NewRef(result);
}
static PyObject*
get_monitor_names(PYNOARG) {
int count = 0;
GLFWmonitor **monitors = glfwGetMonitors(&count);
if (count <= 0 || !monitors) return PyTuple_New(0);
RAII_PyObject(result, PyTuple_New(count)); if (!result) return NULL;
for (int i = 0; i < count; i++) {
const char *name = glfwGetMonitorName(monitors[i]);
const char *description = glfwGetMonitorDescription(monitors[i]);
PyObject *x = Py_BuildValue("ss", name, description);
if (!x) return NULL;
PyTuple_SET_ITEM(result, i, x);
}
return Py_NewRef(result);
}
static PyObject*
primary_monitor_content_scale(PYNOARG) {
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
@ -2643,6 +2660,7 @@ static PyMethodDef module_methods[] = {
{"glfw_get_system_color_theme", (PyCFunction)glfw_get_system_color_theme, METH_VARARGS, ""},
{"glfw_primary_monitor_size", (PyCFunction)primary_monitor_size, METH_NOARGS, ""},
{"glfw_get_monitor_workarea", (PyCFunction)get_monitor_workarea, METH_NOARGS, ""},
{"glfw_get_monitor_names", (PyCFunction)get_monitor_names, METH_NOARGS, ""},
{"glfw_primary_monitor_content_scale", (PyCFunction)primary_monitor_content_scale, METH_NOARGS, ""},
{NULL, NULL, 0, NULL} /* Sentinel */
};

View file

@ -38,6 +38,7 @@
SingleKey,
create_os_window,
free_font_data,
glfw_get_monitor_names,
glfw_get_monitor_workarea,
glfw_init,
glfw_terminate,
@ -220,9 +221,30 @@ def is_panel_kitten() -> bool:
return _is_panel_kitten
def list_monitors() -> None:
monitor_names = glfw_get_monitor_names()
has_descriptions = False
for (name, desc) in monitor_names:
if desc:
has_descriptions = True
break
isatty = sys.stdout.isatty()
for (name, desc) in monitor_names:
if isatty:
name = f'\x1b[32m{name}\x1b[39m' # ]]
print(name)
if desc:
print(f'\t{desc}')
if has_descriptions:
print()
def _run_app(opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = (), talk_fd: int = -1) -> None:
global _is_panel_kitten
_is_panel_kitten = run_app.cached_values_name == 'panel'
if _is_panel_kitten and run_app.layer_shell_config.output_name == 'list':
list_monitors()
return
if is_macos:
global_shortcuts = set_cocoa_global_shortcuts(opts)
if opts.macos_custom_beam_cursor:

View file

@ -639,9 +639,10 @@ def build_panel_cli_spec(defaults: dict[str, str]) -> str:
--output-name
On Wayland or X11, the panel can only be displayed on a single monitor (output) at a time. This allows
The panel can only be displayed on a single monitor (output) at a time. This allows
you to specify which output is used, by name. If not specified the compositor will choose an
output automatically, typically the last output the user interacted with or the primary monitor.
Use the special value :code:`list` to get a list of available outputs.
--class --app-id