Skip to content

Instantly share code, notes, and snippets.

@ahwayakchih
Created September 22, 2018 07:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ahwayakchih/f9b42cac1a8afed28c8e732ab61e3fdf to your computer and use it in GitHub Desktop.
Save ahwayakchih/f9b42cac1a8afed28c8e732ab61e3fdf to your computer and use it in GitHub Desktop.
Proof-of-concept of quick pause/resume active application on Haiku
/*
* Copyright 2018, Marcin Konicki
* All rights reserved. Distributed under the terms of the MIT license.
*/
// This was written after reading:
// https://vermaden.wordpress.com/2018/09/19/freebsd-desktop-part-16-configuration-pause-any-application/
// and then:
// https://dev.haiku-os.org/ticket/14507
// Build with:
// g++ -Wall -o pause_app pause_active_app.cpp -lbe
// After it's ready, add keyboard shortcut through:
// /boot/system/preferences/Shortcuts
// TODO:
// - notification after pausing/resuming
// - option to change priority instead or in addition to pasuing/resuming
// - working option to minimize all app windows
// for: be_roster->GetActiveAppInfo
#include <Roster.h>
// for: get_thread_info
#include <OS.h>
// kill
#include <signal.h>
// for: printf
#include <stdio.h>
// for: errno
#include <errno.h>
// for: strerror
#include <string.h>
// Check if current thread is part of the specified team
bool IsTryingSuspendItself(team_id team)
{
thread_info thread;
if (get_thread_info(find_thread(NULL), &thread) != B_OK)
// Just in case something went wrong but we are in the same team
return true;
return thread.team == team;
}
// Get second argument from command line and try to parse it as integer
team_id GetTeamIdFromArgv(int argc, char** argv)
{
if (argc < 2 || argv[1][0] == '\0')
return 0;
errno = 0;
team_id team = B_BAD_VALUE;
char *end = argv[1];
team = strtol(argv[1], &end, 0);
if (errno != 0) {
return errno;
}
if (end == argv[1])
return B_BAD_VALUE;
if (team < 1)
return B_BAD_VALUE;
return team;
}
// Load app_info for specified team or currently active application
status_t GetAppInfo(app_info* app, team_id team = 0)
{
if (team > 0)
return be_roster->GetRunningAppInfo(team, app);
return be_roster->GetActiveAppInfo(app);
}
port_id GetAppServerPort()
{
status_t status;
app_info info;
status = be_roster->GetAppInfo("application/x-vnd.Haiku-app_server", &info);
if (status != B_OK)
return status;
port_info port;
status = get_port_info(info.port, &port);
if (status != B_OK)
return status;
// printf("App server port name: %s\n", port.name);
// printf("App server port_id: %d\n", port.port);
return info.port;
}
// Minimize all windows of application
// TODO: this currently does not work, and is horribly hacky anyway
// (it uses private protocols and data from app server).
status_t MinimizeTeam(team_id team)
{
port_id port = GetAppServerPort();
if (port < 0)
return port;
// headers/private/app/ServerProtocol.h
const uint32 AS_MINIMIZE_TEAM = 5;
// src/kits/app/link_message.h
const int32 kLinkCode = '_PTL';
uint32 message[4];
message[0] = sizeof(message);
message[1] = AS_MINIMIZE_TEAM;
message[2] = 0;
message[3] = team;
return write_port(port, kLinkCode, &message, sizeof(message));
}
// Suspend running team or resume suspended team
int main(int argc, char** argv)
{
// Make our thread more important, just in case something is taking a lot of CPU time
set_thread_priority(find_thread(NULL), B_URGENT_DISPLAY_PRIORITY);
bool isTTY = isatty(fileno(stdin));
status_t status;
team_id team = GetTeamIdFromArgv(argc, argv);
if (team < 0) {
fprintf(stderr, "ERROR: Incorrect team_id: %s\n", strerror(team));
return team;
}
app_info app;
if ((status = GetAppInfo(&app, team)) != B_OK) {
fprintf(stderr, "ERROR: Could not get information about active application\n");
return status;
}
if (IsTryingSuspendItself(app.team) || (isTTY && team < 1)) {
if (isTTY && team < 1) {
printf("\
USAGE: pause [team_id]\n\
\n\
If started from Terminal, pause needs to get team_id of application to suspend.\n\
You can find it by running `ps` command.\n\
Otherwise it will automatically suspend currently active application (if team_id is not provided).\n\
");
return B_OK;
}
fprintf(stderr, "ERROR: Suspending prevented for safety\n");
return B_PERMISSION_DENIED;
}
// app.team = app.thread = 32;
if (isTTY) printf("team_id: %d and thread_id: %d is... ", app.team, app.thread);
thread_info thread;
if ((status = get_thread_info(app.team, &thread)) != B_OK) {
if (isTTY) printf("not giving up the info\n");
fprintf(stderr, "ERROR: Could not get info about active application (team_id: %d)\n", app.team);
return status;
}
int result = 0;
int sig;
if (thread.state == B_THREAD_SUSPENDED) {
sig = SIGCONT;
if (isTTY) printf("suspended.\nTrying to resume it now... ");
} else {
sig = SIGSTOP;
// if ((status = MinimizeTeam(app.team)) != B_OK)
// fprintf(stderr, "WARNING: could not minimize windows: %s", strerror(status));
if (isTTY) printf("ok.\nTrying to suspend it now... ");
}
result = kill((pid_t)app.team, sig);
int err = errno;
if (result != 0) {
if (isTTY) printf("fail\n");
fprintf(stderr, "ERROR: sending signal failed: %s\n", strerror(err));
return err;
}
if (isTTY) printf("done\n");
return B_OK;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment