~leon_plickat/wlopm

f968900de8e6a32f0c5bd174244376bf3df32cf0 — Leon Henrik Plickat 7 months ago 7ec9605
Better commandline interface, support multiple operations
2 files changed, 245 insertions(+), 110 deletions(-)

M wlopm.1
M wlopm.c
M wlopm.1 => wlopm.1 +16 -13
@@ 1,43 1,46 @@
.TH wlopm 1 2021-03-14 wlopm-0.0.1
.TH wlopm 1 2021-03-17 wlopm-0.0.1

.SH NAME
wlopm - Wayland output power management

.SH DESCRIPTION
wlopm is a simple client implementing zwlr-output-power-management-v1. It can be
used to query and set the output power mode of Wayland outputs.
wlopm is a simple client implementing zwlr-output-power-management-v1. If no
operations are defined, wlopm will list all outputs and their current power
modes.

.SH USAGE
\fBwlopm\fR
\fB-h\fR, \fB--help\fR
.RS 4
List outputs and their power modes.
Print help and exit.
.P
.RE

\fBwlopm --json\fR
\fB-j\fR, \fB--json\fR
.RS 4
List outputs and their power modes, formatted in JSON.
Enable JSON formatting for listing outputs and errors encountered while trying
to set their power modes.
.P
.RE

\fBwlopm on <output-name>\fR
\fB--on\fR <output-name>
.RS 4
Set output power mode to on.
Set the power mode of the output to on.
.P
.RE

\fBwlopm off <output-name>\fR
\fB--off\fR <output-name>
.RS 4
Set output power mode to off.
Set the power mode of the output to off.
.P
.RE

\fBwlopm toggle <output-name>\fR
\fB--toggle\fR <output-name>
.RS 4
Toggle the output power mode.
Toggle the power mode of the output.
.P
.RE


.SH AUTHOR
Leon Henrik Plickat


M wlopm.c => wlopm.c +229 -97
@@ 22,6 22,7 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#include <wayland-client.h>



@@ 31,42 32,55 @@
#define VERSION "0.0.1"

const char usage[] =
	"Usage:\n"
	"\twlopm                        List outputs and their power modes.\n"
	"\twlopm --json                 Format the list as JSON.\n"
	"\twlopm on     <output-name>   Set output power mode to on.\n"
	"\twlopm off    <output-name>   Set output power mode to off.\n"
	"\twlopm toggle <output-name>   Toggle output power mode.\n"
	"Usage: wlopm [options...]\n"
	"  -j, --json                  Use JSON format.\n"
	"  -h, --help                  Print this help text and exit.\n"
	"      --on     <output-name>  Set the power mode of the specified output to on.\n"
	"      --off    <output-name>  Set the power mode of the specified output to off.\n"
	"      --toggle <output-name>  Toggle the power mode of the specified output.\n"
	"\n";

struct Output
{
	struct wl_list link;
	struct wl_output *wl_output;
	struct zxdg_output_v1 *xdg_output;
	struct zwlr_output_power_v1 *wlr_output_power;
	enum zwlr_output_power_v1_mode mode;
	char *name;
	bool operation_failed;
	uint32_t global_name;
};

enum Action
{
	LIST,
	OPERATIONS,
};

enum Power_mode
{
	ON,
	OFF,
	TOGGLE,
};

enum Action action = LIST;
bool json = false;
char *name = NULL;

struct Output
struct Operation
{
	struct wl_list link;
	struct wl_output *wl_output;
	struct zxdg_output_v1 *xdg_output;
	struct zwlr_output_power_v1 *wlr_output_power;
	enum zwlr_output_power_v1_mode mode;
	char *name;
	uint32_t global_name;
	enum Power_mode power_mode;
};

bool json = false;
bool json_prev = false;

struct wl_display *wl_display = NULL;
struct wl_registry *wl_registry = NULL;
struct wl_callback *sync_callback = NULL;

struct wl_list outputs;
struct wl_list operations;

struct zxdg_output_manager_v1 *xdg_output_manager = NULL;
struct zwlr_output_power_manager_v1 *wlr_output_power_manager = NULL;


@@ 86,9 100,7 @@ static void wlr_output_power_handle_mode (void *data, struct zwlr_output_power_v
static void wlr_output_power_handle_failed (void *data, struct zwlr_output_power_v1 *wlr_output_power)
{
	struct Output *output = (struct Output *)data;
	fprintf(stderr, "ERROR: Setting mode for output \"%s\" failed.\n", output->name);
	loop = false;
	ret = EXIT_FAILURE;
	output->operation_failed = true;
}

static const struct zwlr_output_power_v1_listener wlr_output_power_listener = {


@@ 156,7 168,7 @@ static struct Output *output_from_name (const char *str)
{
	struct Output *output;
	wl_list_for_each(output, &outputs, link)
		if ( strcmp(output->name, name) == 0 )
		if ( strcmp(output->name, str) == 0 )
			return output;
	return NULL;
}


@@ 209,22 221,28 @@ static void sync_handle_done (void *data, struct wl_callback *wl_callback, uint3
	}
	else if ( sync == 1 )
	{
		if ( action == LIST )
		if (wl_list_empty(&operations))
		{
			/* The operations list is empty, so let's just list all
			 * outputs and their current power mode.
			 */
			struct Output *output;
			if (json)
			{
				fputs("[\n", stdout);
				uint32_t i = 0, len = (uint32_t)wl_list_length(&outputs);
				fputs("[", stdout);
				wl_list_for_each(output, &outputs, link)
				{
					fprintf(stdout, "  {\n    \"output\": \"%s\",\n    \"power-mode\": \"%s\"\n  }%s\n",
					fprintf(stdout,
							"%s\n  {\n"
							"    \"output\": \"%s\",\n"
							"    \"power-mode\": \"%s\"\n"
							"  }",
							json_prev ? "," : "",
							output->name,
							power_mode_to_string(output->mode),
							i < (len - 1) ? "," : "");
					i++;
							power_mode_to_string(output->mode));
					json_prev = true;
				}
				fputs("]\n", stdout);
				fputs("\n]\n", stdout);
			}
			else
				wl_list_for_each(output, &outputs, link)


@@ 234,87 252,217 @@ static void sync_handle_done (void *data, struct wl_callback *wl_callback, uint3
		}
		else
		{
			const struct Output *output = output_from_name(name);
			if ( output == NULL )
			{
				fprintf(stdout, "ERROR: No output with name \"%s\".\n", name);
				ret = EXIT_FAILURE;
				loop = false;
				return;
			}
			/* There are operations in the operations list. We have
			 * things to do!
			 */

			if (json)
				fputs(
						"{\n"
						"  \"errors\": [",
						stdout);

			enum zwlr_output_power_v1_mode new_mode;
			switch (action)
			struct Operation *operation;
			wl_list_for_each(operation, &operations, link)
			{
				case ON:
					new_mode = ZWLR_OUTPUT_POWER_V1_MODE_ON;
					break;
				const struct Output *output = output_from_name(operation->name);
				if ( output == NULL )
				{
					if (json)
					{
						fprintf(stdout,
								"%s\n    {\n"
								"      \"output\": \"%s\",\n"
								"      \"error\": \"output does not exist\"\n"
								"    }",
								json_prev ? "," : "",
								operation->name);
						json_prev = true;
					}
					else
						fprintf(stderr, "ERROR: Output '%s' does not exist.\n",
								operation->name);
					continue;
				}

				case OFF:
					new_mode = ZWLR_OUTPUT_POWER_V1_MODE_OFF;
					break;
				enum zwlr_output_power_v1_mode new_mode;
				switch (operation->power_mode)
				{
					case ON:
						new_mode = ZWLR_OUTPUT_POWER_V1_MODE_ON;
						break;

				case TOGGLE:
					if ( output->mode == ZWLR_OUTPUT_POWER_V1_MODE_ON )
					case OFF:
						new_mode = ZWLR_OUTPUT_POWER_V1_MODE_OFF;
					else
						new_mode = ZWLR_OUTPUT_POWER_V1_MODE_ON;
					break;
						break;

					case TOGGLE:
						if ( output->mode == ZWLR_OUTPUT_POWER_V1_MODE_ON )
							new_mode = ZWLR_OUTPUT_POWER_V1_MODE_OFF;
						else
							new_mode = ZWLR_OUTPUT_POWER_V1_MODE_ON;
						break;
				}

				case LIST:
					/* unreachable */
					break;
				zwlr_output_power_v1_set_mode(output->wlr_output_power, new_mode);
			}

			zwlr_output_power_v1_set_mode(output->wlr_output_power, new_mode);

			/* We need to sync yet another time because setting the
			 * power mode might fail.
			 * power mode might fail and we want to display those
			 * error messages.
			 */
			sync_callback = wl_display_sync(wl_display);
			wl_callback_add_listener(sync_callback, &sync_callback_listener, NULL);
		}
	}
	else
	{

		struct Output *output;
		wl_list_for_each(output, &outputs, link)
			if (output->operation_failed)
			{
				if (json)
				{
					fprintf(stdout,
							"%s\n    {\n"
							"      \"output\": \"%s\","
							"      \"error\": \"setting power mode failed\"\n"
							"    }",
							json_prev ? "," : "",
							output->name);
					json_prev = true;
				}
				else
					fprintf(stderr, "ERROR: Setting power mode for output '%s' failed.\n",
							output->name);
			}

		if (json)
			fputs(
					"\n  ]\n"
					"}\n",
					stdout);
		loop = false;
	}
	sync++;
}

int main(int argc, char *argv[])
static void destroy_all_outputs (void)
{
	struct Output *output, *tmp;
	wl_list_for_each_safe(output, tmp, &outputs, link)
	{
		if ( output->wlr_output_power != NULL )
			zwlr_output_power_v1_destroy(output->wlr_output_power);
		if ( output->xdg_output != NULL )
			zxdg_output_v1_destroy(output->xdg_output);
		wl_output_destroy(output->wl_output);
		wl_list_remove(&output->link);
		free(output->name);
		free(output);
	}
}

static void destroy_all_operations (void)
{
	switch (argc)
	struct Operation *operation, *tmp;
	wl_list_for_each_safe(operation, tmp, &operations, link)
	{
		case 1:
			break;
		wl_list_remove(&operation->link);
		free(operation->name);
		free(operation);
	}
}

		case 2:
			if ( strcmp(argv[1], "--json") == 0 )
				json = true;
			else
static bool create_operation (const char *name, enum Power_mode power_mode)
{
	struct Operation *operation = calloc(1, sizeof(struct Operation));
	if ( operation == NULL )
	{
		fprintf(stderr, "ERROR: calloc: %s\n", strerror(errno));
		return false;
	}

	operation->name = strdup(name);
	if ( operation->name == NULL )
	{
		fprintf(stderr, "ERROR: calloc: %s\n", strerror(errno));
		free(operation);
		return false;
	}

	operation->power_mode = power_mode;

	wl_list_insert(&operations, &operation->link);
	return true;
}

int main(int argc, char *argv[])
{
	wl_list_init(&operations);
	for (int i = 1; i < argc; i++)
	{
		if ( strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0 )
		{
			destroy_all_operations();
			fputs(usage, stderr);
			return EXIT_SUCCESS;
		}
		else if ( strcmp(argv[i], "-j") == 0 || strcmp(argv[i], "--json") == 0 )
			json = true;
		else if ( strcmp(argv[i], "--on") == 0 )
		{
			if ( i == argc - 1 )
			{
				fputs(usage, stderr);
				fputs("ERROR: '--on' needs an output name.\n", stderr);
				destroy_all_operations();
				return EXIT_FAILURE;
			}
			break;

		case 3:
			if ( strcmp(argv[1], "on") == 0 )
				action = ON;
			else if ( strcmp(argv[1], "off") == 0 )
				action = OFF;
			else if ( strcmp(argv[1], "toggle") == 0 )
				action = TOGGLE;
			else
			if (! create_operation(argv[i+1], ON))
			{
				fputs(usage, stderr);
				destroy_all_operations();
				return EXIT_FAILURE;
			}
			name = strdup(argv[2]);
			break;

		default:
			fputs(usage, stderr);
			i++;
		}
		else if ( strcmp(argv[i], "--off") == 0 )
		{
			if ( i == argc - 1 )
			{
				fputs("ERROR: '--off' needs an output name.\n", stderr);
				destroy_all_operations();
				return EXIT_FAILURE;
			}
			if (! create_operation(argv[i+1], OFF))
			{
				destroy_all_operations();
				return EXIT_FAILURE;
			}
			i++;
		}
		else if ( strcmp(argv[i], "--toggle") == 0 )
		{
			if ( i == argc - 1 )
			{
				fputs("ERROR: '--toggle' needs an output name.\n", stderr);
				destroy_all_operations();
				return EXIT_FAILURE;
			}
			if (! create_operation(argv[i+1], TOGGLE))
			{
				destroy_all_operations();
				return EXIT_FAILURE;
			}
			i++;
		}
		else
		{
			fprintf(stderr, "ERROR: Unknown option '%s'\n", argv[i]);
			destroy_all_operations();
			return EXIT_FAILURE;
		}
	}

	/* We query the display name here instead of letting wl_display_connect()


@@ 326,8 474,6 @@ int main(int argc, char *argv[])
	if ( display_name == NULL )
	{
		fputs("ERROR: WAYLAND_DISPLAY is not set.\n", stderr);
		if ( name != NULL )
			free(name);
		return EXIT_FAILURE;
	}



@@ 335,8 481,6 @@ int main(int argc, char *argv[])
	if ( wl_display == NULL )
	{
		fputs("ERROR: Can not connect to wayland display.\n", stderr);
		if ( name != NULL )
			free(name);
		return EXIT_FAILURE;
	}



@@ 350,21 494,9 @@ int main(int argc, char *argv[])

	while ( loop && wl_display_dispatch(wl_display) > 0 );

	struct Output *output, *tmp;
	wl_list_for_each_safe(output, tmp, &outputs, link)
	{
		if ( output->wlr_output_power != NULL )
			zwlr_output_power_v1_destroy(output->wlr_output_power);
		if ( output->xdg_output != NULL )
			zxdg_output_v1_destroy(output->xdg_output);
		wl_output_destroy(output->wl_output);
		wl_list_remove(&output->link);
		free(output->name);
		free(output);
	}
	destroy_all_operations();
	destroy_all_outputs();

	if ( name != NULL )
		free(name);
	if ( sync_callback != NULL )
		wl_callback_destroy(sync_callback);
	if ( wlr_output_power_manager != NULL )