Skip to content

Instantly share code, notes, and snippets.

@sulincix
Last active February 11, 2026 10:06
Show Gist options
  • Select an option

  • Save sulincix/0173bc3fa721dec9a223d433896a14be to your computer and use it in GitHub Desktop.

Select an option

Save sulincix/0173bc3fa721dec9a223d433896a14be to your computer and use it in GitHub Desktop.
Wayland xdg-desktop-portal screenshot in C
#include <gio/gio.h>
#include <stdio.h>
/* gcc glib_screenshot.c -o screenshot `pkg-config --cflags --libs gio-2.0 glib-2.0` */
static GMainLoop *loop;
/* Response signal handler */
static void
on_response(GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
if (g_strcmp0(signal_name, "Response") == 0) {
guint32 response;
GVariant *results;
g_variant_get(parameters, "(u@a{sv})", &response, &results);
if (response == 0) {
GVariant *uri_variant = g_variant_lookup_value(results, "uri", G_VARIANT_TYPE_STRING);
if (uri_variant) {
const gchar *uri = g_variant_get_string(uri_variant, NULL);
g_print("Screenshot URI: %s\n", uri);
g_variant_unref(uri_variant);
}
} else {
g_print("Failed to take a screenshot.\n");
}
g_variant_unref(results);
g_main_loop_quit(loop);
}
}
int main()
{
GError *error = NULL;
/* Connect to session bus */
GDBusConnection *bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
if (!bus) {
g_printerr("Failed to connect to session bus: %s\n", error->message);
g_error_free(error);
return 1;
}
/* Create Screenshot proxy */
GDBusProxy *proxy = g_dbus_proxy_new_sync(
bus,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Screenshot",
NULL,
&error
);
if (!proxy) {
g_printerr("Failed to create proxy: %s\n", error->message);
g_error_free(error);
return 1;
}
/* Build options dictionary */
GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
g_variant_builder_add(&builder, "{sv}",
"handle_token",
g_variant_new_string("my_unique_token"));
g_variant_builder_add(&builder, "{sv}",
"interactive",
g_variant_new_boolean(FALSE));
GVariant *parameters = g_variant_new("(sa{sv})", "", &builder);
/* Call Screenshot method */
GVariant *result = g_dbus_proxy_call_sync(
proxy,
"Screenshot",
parameters,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error
);
if (!result) {
g_printerr("Screenshot call failed: %s\n", error->message);
g_error_free(error);
return 1;
}
/* Extract request object path */
const gchar *handle;
g_variant_get(result, "(o)", &handle);
g_variant_unref(result);
/* Create Request proxy */
GDBusProxy *request_proxy = g_dbus_proxy_new_sync(
bus,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"org.freedesktop.portal.Desktop",
handle,
"org.freedesktop.portal.Request",
NULL,
&error
);
if (!request_proxy) {
g_printerr("Failed to create request proxy: %s\n", error->message);
g_error_free(error);
return 1;
}
/* Listen for Response signal */
g_signal_connect(request_proxy, "g-signal", G_CALLBACK(on_response), NULL);
loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(loop);
/* Cleanup */
g_object_unref(request_proxy);
g_object_unref(proxy);
g_object_unref(bus);
g_main_loop_unref(loop);
return 0;
}
#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* gcc glib_screenshot.c -o screenshot `pkg-config --cflags --libs dbus-1` */
static void listen_for_response(DBusConnection *conn, const char *request_path);
int main() {
DBusError err;
dbus_error_init(&err);
DBusConnection *conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
if (dbus_error_is_set(&err)) {
fprintf(stderr, "Connection Error: %s\n", err.message);
dbus_error_free(&err);
return 1;
}
if (!conn) return 1;
DBusMessage *msg = dbus_message_new_method_call(
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Screenshot",
"Screenshot"
);
if (!msg) return 1;
const char *parent_window = "";
DBusMessageIter args;
dbus_message_iter_init_append(msg, &args);
// string parent_window
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &parent_window);
// a{sv} options
DBusMessageIter dict;
dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &dict);
// handle_token
DBusMessageIter entry;
dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
const char *key = "handle_token";
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
DBusMessageIter variant;
dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "s", &variant);
const char *token = "mytoken";
dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &token);
dbus_message_iter_close_container(&entry, &variant);
dbus_message_iter_close_container(&dict, &entry);
dbus_message_iter_close_container(&args, &dict);
DBusPendingCall *pending;
if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) {
fprintf(stderr, "Out Of Memory!\n");
return 1;
}
if (!pending) return 1;
dbus_connection_flush(conn);
dbus_message_unref(msg);
dbus_pending_call_block(pending);
DBusMessage *reply = dbus_pending_call_steal_reply(pending);
dbus_pending_call_unref(pending);
if (!reply) return 1;
const char *request_path;
if (!dbus_message_get_args(reply, &err,
DBUS_TYPE_OBJECT_PATH, &request_path,
DBUS_TYPE_INVALID)) {
fprintf(stderr, "Reply Error: %s\n", err.message);
dbus_error_free(&err);
return 1;
}
printf("Request path: %s\n", request_path);
dbus_message_unref(reply);
listen_for_response(conn, request_path);
return 0;
}
static void listen_for_response(DBusConnection *conn, const char *request_path) {
DBusError err;
dbus_error_init(&err);
char match[512];
snprintf(match, sizeof(match),
"type='signal',interface='org.freedesktop.portal.Request',member='Response',path='%s'",
request_path);
dbus_bus_add_match(conn, match, &err);
dbus_connection_flush(conn);
printf("Waiting for Response signal...\n");
while (1) {
dbus_connection_read_write(conn, 0);
DBusMessage *msg = dbus_connection_pop_message(conn);
if (!msg) {
usleep(100);
continue;
}
if (dbus_message_is_signal(msg,
"org.freedesktop.portal.Request",
"Response")) {
DBusMessageIter args;
if (!dbus_message_iter_init(msg, &args)) {
printf("Signal has no arguments\n");
} else {
dbus_uint32_t response;
dbus_message_iter_get_basic(&args, &response);
if (response == 0) {
dbus_message_iter_next(&args);
DBusMessageIter dict;
dbus_message_iter_recurse(&args, &dict);
while (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
DBusMessageIter entry;
dbus_message_iter_recurse(&dict, &entry);
const char *key;
dbus_message_iter_get_basic(&entry, &key);
if (strcmp(key, "uri") == 0) {
dbus_message_iter_next(&entry);
DBusMessageIter variant;
dbus_message_iter_recurse(&entry, &variant);
const char *uri;
dbus_message_iter_get_basic(&variant, &uri);
printf("Screenshot URI: %s\n", uri);
}
dbus_message_iter_next(&dict);
}
} else {
printf("Failed to take screenshot\n");
}
}
dbus_message_unref(msg);
break;
}
dbus_message_unref(msg);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment