From cb57c8d92a179e4018c4492ca652136045a92506 Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Mon, 11 Jul 2022 13:12:29 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20?= =?UTF-8?q?=D1=81=20=D0=BC=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80=D0=B0=D0=BC?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- arguments.c | 10 +- parameter.h | 6 +- settings.c | 172 +++- value.h | 12 +- xrandr.c | 2129 +++++++++++++++++++++++++++++++++++++++++++++++ xrandr.h | 30 + xrandr_broker.c | 29 + 7 files changed, 2367 insertions(+), 21 deletions(-) create mode 100644 xrandr.c create mode 100644 xrandr.h create mode 100644 xrandr_broker.c diff --git a/arguments.c b/arguments.c index 981654f..ccec590 100644 --- a/arguments.c +++ b/arguments.c @@ -16,14 +16,22 @@ void settingsLoad() addParameterKey(PARAMETER_SERVER, "/v:", true, true); addParameterKey(PARAMETER_USERNAME, "/u:", true, true); addParameterKey(PARAMETER_PASSWORD, "/p:", true, true); - addParameterKey(PARAMETER_MULTIMONITOR, "/multimon", true, true); + addParameterKey(PARAMETER_CERTIGNORE, "/cert-ignore", false, true); + addParameterKey(PARAMETER_THEMES, "-themes", false, true); + addParameterKey(PARAMETER_WALLPAPER, "-wallpaper", false, true); addParameterKey(PARAMETER_FULLSCREEN, "/f", true, true); + addParameterKey(PARAMETER_MULTIMONITOR, "/multimon", true, true); addParameterKey(PARAMETER_AUTHENTICATION, "-authentication", true, true); addParameterKey(PARAMETER_SECURITY, "/sec:", true, true); addParameterValue(PARAMETER_SECURITY, VALUE_SECURITY_TLS, "tls", true); addParameterValue(PARAMETER_SECURITY, VALUE_SECURITY_RDP, "rdp", false); addParameterValue(PARAMETER_SECURITY, VALUE_SECURITY_NLA, "nla", false); addParameterValue(PARAMETER_SECURITY, VALUE_SECURITY_EXT, "ext", false); + addParameterKey(PARAMETER_BITSPERPIXEL, "/bpp:", true, true); + addParameterValue(PARAMETER_BITSPERPIXEL, VALUES_BITSPERPIXEL_8, "8", true); + addParameterValue(PARAMETER_BITSPERPIXEL, VALUES_BITSPERPIXEL_16, "16", false); + addParameterValue(PARAMETER_BITSPERPIXEL, VALUES_BITSPERPIXEL_24, "24", false); + addParameterValue(PARAMETER_BITSPERPIXEL, VALUES_BITSPERPIXEL_32, "32", false); } void settingsFree() diff --git a/parameter.h b/parameter.h index 6853eef..9685f87 100644 --- a/parameter.h +++ b/parameter.h @@ -14,10 +14,14 @@ typedef enum PARAMETER_SERVER, PARAMETER_USERNAME, PARAMETER_PASSWORD, + PARAMETER_CERTIGNORE, + PARAMETER_THEMES, + PARAMETER_WALLPAPER, PARAMETER_MULTIMONITOR, PARAMETER_FULLSCREEN, PARAMETER_AUTHENTICATION, - PARAMETER_SECURITY + PARAMETER_SECURITY, + PARAMETER_BITSPERPIXEL, } Parameter; #endif /* PARAMETERS_H_ */ diff --git a/settings.c b/settings.c index f84bcd1..0c02aa4 100644 --- a/settings.c +++ b/settings.c @@ -10,6 +10,8 @@ #include #include +#include "xrandr.h" + #include "settings.h" #include "arguments.h" #include "node_settings.h" @@ -19,7 +21,6 @@ void toggleActive(Ihandle *self, char *name) IupSetInt(IupGetDialogChild(self, name), "ACTIVE", !IupGetInt(IupGetDialogChild(self, name), "ACTIVE")); } - static int settingsClose(Ihandle *self) { resetChangeSettings(); @@ -54,6 +55,24 @@ static int settingsTglAuthentication(Ihandle *self) return IUP_DEFAULT; } +static int settingsTglCertIgnore(Ihandle *self) +{ + changeParameter(PARAMETER_CERTIGNORE); + return IUP_DEFAULT; +} + +static int settingsTglThemes(Ihandle *self) +{ + changeParameter(PARAMETER_THEMES); + return IUP_DEFAULT; +} + +static int settingsTglWallpaper(Ihandle *self) +{ + changeParameter(PARAMETER_WALLPAPER); + return IUP_DEFAULT; +} + static int settingsUseSecurity(Ihandle *self) { changeParameter(PARAMETER_SECURITY); @@ -96,29 +115,92 @@ static void settingsChooseSecurity(Ihandle *self, int state) } } +static int settingsUseBitsPerPixel(Ihandle *self) +{ + changeParameter(PARAMETER_BITSPERPIXEL); + toggleActive(self, "BITSPERPIXEL_8"); + toggleActive(self, "BITSPERPIXEL_16"); + toggleActive(self, "BITSPERPIXEL_24"); + toggleActive(self, "BITSPERPIXEL_32"); + + return IUP_DEFAULT; +} + +static void settingsChooseBitsPerPixel(Ihandle *self, int state) +{ + if (state == 1) + { + int tool_index = IupGetInt(self, "TOOLINDEX"); + switch (tool_index) + { + case 0: + { + changeValue(PARAMETER_BITSPERPIXEL, VALUES_BITSPERPIXEL_8); + break; + } + case 1: + { + changeValue(PARAMETER_BITSPERPIXEL, VALUES_BITSPERPIXEL_16); + break; + } + case 2: + { + changeValue(PARAMETER_BITSPERPIXEL, VALUES_BITSPERPIXEL_24); + break; + } + case 3: + { + changeValue(PARAMETER_BITSPERPIXEL, VALUES_BITSPERPIXEL_32); + break; + } + } + } +} + +static int settingsChooseMonitor(Ihandle *self) +{ +// printf("%d\n", IupGetInt(IupGetDialogChild(self, "VALUE"), "ACTIVE")); + return IUP_DEFAULT; +} + /* * Блок настроек */ static Ihandle* settingsBoxCheckbox() { - Ihandle *tglMultimonitor, *tglFullscreen, *tglAuthentication; + Ihandle *tglMultimonitor, *tglFullscreen, *tglAuthentication, *tglCertIgnore, *tglThemes, *tglWallpaper; tglMultimonitor = IupToggle("Несколько мониторов", NULL); tglFullscreen = IupToggle("На весь экран", NULL); tglAuthentication = IupToggle("Аутентификация", NULL); + tglCertIgnore = IupToggle("Игнорировать сертификат", NULL); + tglThemes = IupToggle("Отключить темы", NULL); + tglWallpaper = IupToggle("Отключить обои", NULL); IupSetInt(tglMultimonitor, "VALUE", getSetParameter(PARAMETER_MULTIMONITOR)); IupSetInt(tglFullscreen, "VALUE", getSetParameter(PARAMETER_FULLSCREEN)); IupSetInt(tglAuthentication, "VALUE", getSetParameter(PARAMETER_AUTHENTICATION)); + IupSetInt(tglCertIgnore, "VALUE", getSetParameter(PARAMETER_CERTIGNORE)); + IupSetInt(tglThemes, "VALUE", getSetParameter(PARAMETER_THEMES)); + IupSetInt(tglWallpaper, "VALUE", getSetParameter(PARAMETER_WALLPAPER)); - return IupHbox(IupSetAttributes(IupFrame(IupVbox( - IupSetCallbacks(IupSetAttributes(tglMultimonitor, "NAME=SETTINGS_TGL_MULTIMONITOR"), "ACTION", - (Icallback) settingsTglMultimonitor, NULL), - IupSetCallbacks(IupSetAttributes(tglFullscreen, "NAME=SETTINGS_TGL_FULLSCREEN"), "ACTION", - (Icallback) settingsTglFullscreen, NULL), - IupSetCallbacks(IupSetAttributes(tglAuthentication, "NAME=SETTINGS_TGL_AUTHENTICATION"), "ACTION", - (Icallback) settingsTglAuthentication, NULL), - NULL)), "TITLE=\"Общие\""), NULL); + return IupHbox( + IupSetAttributes( + IupFrame( + IupVbox( + IupSetCallbacks(IupSetAttributes(tglMultimonitor, "NAME=SETTINGS_TGL_MULTIMONITOR"), "ACTION", + (Icallback) settingsTglMultimonitor, NULL), + IupSetCallbacks(IupSetAttributes(tglFullscreen, "NAME=SETTINGS_TGL_FULLSCREEN"), "ACTION", + (Icallback) settingsTglFullscreen, NULL), + IupSetCallbacks(IupSetAttributes(tglAuthentication, "NAME=SETTINGS_TGL_AUTHENTICATION"), "ACTION", + (Icallback) settingsTglAuthentication, NULL), + IupSetCallbacks(IupSetAttributes(tglCertIgnore, "NAME=SETTINGS_TGL_AUTHENTICATION"), "ACTION", + (Icallback) settingsTglCertIgnore, NULL), + IupSetCallbacks(IupSetAttributes(tglThemes, "NAME=SETTINGS_TGL_THEMES"), "ACTION", (Icallback) settingsTglThemes, + NULL), + IupSetCallbacks(IupSetAttributes(tglWallpaper, "NAME=SETTINGS_TGL_WALLPAPER"), "ACTION", + (Icallback) settingsTglWallpaper, NULL), + NULL)), "TITLE=\"Общие\""), NULL); } static Ihandle* settingsBoxSecurity() @@ -129,7 +211,7 @@ static Ihandle* settingsBoxSecurity() tglSecurity = IupToggle("Использовать", NULL); IupSetInt(tglSecurity, "VALUE", getSetParameter(PARAMETER_SECURITY)); - IupSetCallback(tglSecurity, "ACTION", (Icallback)settingsUseSecurity); + IupSetCallback(tglSecurity, "ACTION", (Icallback) settingsUseSecurity); tglTLS = IupToggle("TLS", NULL); IupSetInt(tglTLS, "VALUE", getSetValue(PARAMETER_SECURITY, VALUE_SECURITY_TLS)); @@ -144,19 +226,73 @@ static Ihandle* settingsBoxSecurity() IupSetInt(tglEXT, "VALUE", getSetValue(PARAMETER_SECURITY, VALUE_SECURITY_EXT)); IupSetInt(tglEXT, "ACTIVE", getSetParameter(PARAMETER_SECURITY)); - grdSecurity = IupRadio(IupGridBox( - IupSetCallbacks(IupSetAttributes(tglTLS, "NAME=SECURITY_TLS, TOOLINDEX=0"), "ACTION", (Icallback)settingsChooseSecurity, NULL), - IupSetCallbacks(IupSetAttributes(tglRDP, "NAME=SECURITY_RDP, TOOLINDEX=1"), "ACTION", (Icallback)settingsChooseSecurity, NULL), - IupSetCallbacks(IupSetAttributes(tglNLA, "NAME=SECURITY_NLA, TOOLINDEX=2"), "ACTION", (Icallback)settingsChooseSecurity, NULL), - IupSetCallbacks(IupSetAttributes(tglEXT, "NAME=SECURITY_EXT, TOOLINDEX=3"), "ACTION", (Icallback)settingsChooseSecurity, NULL), - NULL)); + grdSecurity = IupRadio( + IupGridBox( + IupSetCallbacks(IupSetAttributes(tglTLS, "NAME=SECURITY_TLS, TOOLINDEX=0"), "ACTION", (Icallback) settingsChooseSecurity, NULL), + IupSetCallbacks(IupSetAttributes(tglRDP, "NAME=SECURITY_RDP, TOOLINDEX=1"), "ACTION", (Icallback) settingsChooseSecurity, NULL), + IupSetCallbacks(IupSetAttributes(tglNLA, "NAME=SECURITY_NLA, TOOLINDEX=2"), "ACTION", (Icallback) settingsChooseSecurity, NULL), + IupSetCallbacks(IupSetAttributes(tglEXT, "NAME=SECURITY_EXT, TOOLINDEX=3"), "ACTION", (Icallback) settingsChooseSecurity, NULL), + NULL)); return IupHbox(IupSetAttributes(IupFrame(IupVbox(tglSecurity, grdSecurity, NULL)), "TITLE=\"Протокол безопасности\", MARGIN=15x10"), NULL); } +static Ihandle* settingsBoxBitsPerPixel() +{ + Ihandle *tglBitsPerPixel; + Ihandle *tgl8, *tgl16, *tgl24, *tgl32; + Ihandle *grdBitsPerPixel; + + tglBitsPerPixel = IupToggle("Использовать", NULL); + IupSetInt(tglBitsPerPixel, "VALUE", getSetParameter(PARAMETER_BITSPERPIXEL)); + IupSetCallback(tglBitsPerPixel, "ACTION", (Icallback) settingsUseBitsPerPixel); + + tgl8 = IupToggle("8 бит", NULL); + IupSetInt(tgl8, "VALUE", getSetValue(PARAMETER_BITSPERPIXEL, VALUES_BITSPERPIXEL_8)); + IupSetInt(tgl8, "ACTIVE", getSetParameter(PARAMETER_BITSPERPIXEL)); + tgl16 = IupToggle("16 бит", NULL); + IupSetInt(tgl16, "VALUE", getSetValue(PARAMETER_BITSPERPIXEL, VALUES_BITSPERPIXEL_16)); + IupSetInt(tgl16, "ACTIVE", getSetParameter(PARAMETER_BITSPERPIXEL)); + tgl24 = IupToggle("24 бит", NULL); + IupSetInt(tgl24, "VALUE", getSetValue(PARAMETER_BITSPERPIXEL, VALUES_BITSPERPIXEL_24)); + IupSetInt(tgl24, "ACTIVE", getSetParameter(PARAMETER_BITSPERPIXEL)); + tgl32 = IupToggle("32 бит", NULL); + IupSetInt(tgl32, "VALUE", getSetValue(PARAMETER_BITSPERPIXEL, VALUES_BITSPERPIXEL_32)); + IupSetInt(tgl32, "ACTIVE", getSetParameter(PARAMETER_BITSPERPIXEL)); + + grdBitsPerPixel = IupRadio( + IupGridBox(IupSetCallbacks(IupSetAttributes(tgl8, "NAME=BITSPERPIXEL_8, TOOLINDEX=0"), "ACTION", (Icallback) settingsChooseBitsPerPixel, + NULL), IupSetCallbacks(IupSetAttributes(tgl16, "NAME=BITSPERPIXEL_16, TOOLINDEX=1"), "ACTION", (Icallback) settingsChooseBitsPerPixel, + NULL), IupSetCallbacks(IupSetAttributes(tgl24, "NAME=BITSPERPIXEL_24, TOOLINDEX=2"), "ACTION", (Icallback) settingsChooseBitsPerPixel, + NULL), IupSetCallbacks(IupSetAttributes(tgl32, "NAME=BITSPERPIXEL_32, TOOLINDEX=3"), "ACTION", (Icallback) settingsChooseBitsPerPixel, + NULL), + NULL)); + + return IupHbox(IupSetAttributes(IupFrame(IupVbox(tglBitsPerPixel, grdBitsPerPixel, NULL)), "TITLE=\"Глубина цвета\", MARGIN=15x10"), NULL); +} + +static Ihandle* settingsBoxMonitor() +{ + Ihandle *ddMonitor; + + ddMonitor = IupList(NULL); + + x_info *monitors = getXInfo(); + + for (short i = 0; i < monitors->count; ++i) + { + IupSetAttribute(ddMonitor, monitors->monitor[i].ptrIndex, monitors->monitor[i].ptrName); + } + + free(monitors); + + return IupSetCallbacks(IupSetAttributes(ddMonitor, + "DROPDOWN=YES, VALUE=1"), "ACTION", (Icallback) settingsChooseMonitor, NULL); +} + static Ihandle* settingsHorizontalBox() { - return IupSetAttributes(IupHbox(settingsBoxCheckbox(), settingsBoxSecurity(), NULL), "MARGIN=5x5"); + return IupSetAttributes(IupHbox(IupVbox(settingsBoxCheckbox(), settingsBoxMonitor(), NULL), settingsBoxSecurity(), settingsBoxBitsPerPixel(), NULL), "MARGIN=5x5"); } /* diff --git a/value.h b/value.h index c8f174a..7863db3 100644 --- a/value.h +++ b/value.h @@ -10,7 +10,17 @@ typedef enum { - VALUE_SERVER, VALUE_USERNAME, VALUE_PASSWORD, VALUE_SECURITY_TLS, VALUE_SECURITY_RDP, VALUE_SECURITY_NLA, VALUE_SECURITY_EXT, + VALUE_SERVER, + VALUE_USERNAME, + VALUE_PASSWORD, + VALUE_SECURITY_TLS, + VALUE_SECURITY_RDP, + VALUE_SECURITY_NLA, + VALUE_SECURITY_EXT, + VALUES_BITSPERPIXEL_8, + VALUES_BITSPERPIXEL_16, + VALUES_BITSPERPIXEL_24, + VALUES_BITSPERPIXEL_32 } Value; #endif /* VALUE_H_ */ diff --git a/xrandr.c b/xrandr.c new file mode 100644 index 0000000..b529932 --- /dev/null +++ b/xrandr.c @@ -0,0 +1,2129 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "xrandr.h" + +static char *program_name; +static Display *dpy; +static Window root; +static int screen = -1; +static Bool verbose = False; +static Bool automatic = False; +static Bool grab_server = True; +static Bool no_primary = False; +static int filter_type = -1; + +static const char *filter_names[2] = +{ "bilinear", "nearest" }; + +static const char *direction[5] = +{ "normal", "left", "inverted", "right", "\n" }; + +static void _X_NORETURN _X_ATTRIBUTE_PRINTF(1, 2) fatal(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, ap); + va_end(ap); + exit(1); +} + +static void _X_ATTRIBUTE_PRINTF(1, 2) warning(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, ap); + va_end(ap); +} + +static void _X_NORETURN _X_ATTRIBUTE_PRINTF(1, 2) argerr(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, ap); + fprintf(stderr, "Try '%s --help' for more information.\n", program_name); + va_end(ap); + exit(1); +} + +static inline double dmin(double x, double y) +{ + return x < y ? x : y; +} + +static const char* +rotation_name(Rotation rotation) +{ + int i; + + if ((rotation & 0xf) == 0) + return "normal"; + for (i = 0; i < 4; i++) + if (rotation & (1 << i)) + return direction[i]; + return "invalid rotation"; +} + +static const char* +reflection_name(Rotation rotation) +{ + rotation &= (RR_Reflect_X | RR_Reflect_Y); + switch (rotation) + { + case 0: + return "none"; + case RR_Reflect_X: + return "X axis"; + case RR_Reflect_Y: + return "Y axis"; + case RR_Reflect_X | RR_Reflect_Y: + return "X and Y axis"; + } + return "invalid reflection"; +} + +typedef enum _relation +{ + relation_left_of, relation_right_of, relation_above, relation_below, relation_same_as, +} relation_t; + +typedef struct +{ + int x, y, width, height; +} rectangle_t; + +typedef struct +{ + int x1, y1, x2, y2; +} box_t; + +typedef struct +{ + int x, y; +} point_t; + +typedef enum _changes +{ + changes_none = 0, + changes_crtc = (1 << 0), + changes_mode = (1 << 1), + changes_relation = (1 << 2), + changes_position = (1 << 3), + changes_rotation = (1 << 4), + changes_reflection = (1 << 5), + changes_automatic = (1 << 6), + changes_refresh = (1 << 7), + changes_property = (1 << 8), + changes_transform = (1 << 9), + changes_panning = (1 << 10), + changes_gamma = (1 << 11), + changes_primary = (1 << 12), + changes_filter = (1 << 13), +} changes_t; + +typedef enum _name_kind +{ + name_none = 0, name_string = (1 << 0), name_xid = (1 << 1), name_index = (1 << 2), name_preferred = (1 << 3), +} name_kind_t; + +typedef struct +{ + name_kind_t kind; + char *string; + XID xid; + int index; +} name_t; + +typedef struct _crtc crtc_t; +typedef struct _output output_t; +typedef struct _transform transform_t; +typedef struct _umode umode_t; +typedef struct _output_prop output_prop_t; +typedef struct _provider provider_t; +typedef struct _monitors monitors_t; +typedef struct _umonitor umonitor_t; + +struct _transform +{ + XTransform transform; + const char *filter; + int nparams; + XFixed *params; +}; + +struct _crtc +{ + name_t crtc; + Bool changing; + XRRCrtcInfo *crtc_info; + + XRRModeInfo *mode_info; + XRRPanning *panning_info; + int x; + int y; + Rotation rotation; + output_t **outputs; + int noutput; + transform_t current_transform, pending_transform; +}; + +struct _output_prop +{ + struct _output_prop *next; + char *name; + char *value; +}; + +struct _output +{ + struct _output *next; + + changes_t changes; + + output_prop_t *props; + + name_t output; + XRROutputInfo *output_info; + + name_t crtc; + crtc_t *crtc_info; + crtc_t *current_crtc_info; + + name_t mode; + double refresh; + XRRModeInfo *mode_info; + + name_t addmode; + + relation_t relation; + char *relative_to; + + int x, y; + Rotation rotation; + + XRRPanning panning; + + Bool automatic; + int scale_from_w, scale_from_h; + transform_t transform; + + struct + { + float red; + float green; + float blue; + } gamma; + + float brightness; + + Bool primary; + + Bool found; +}; + +typedef enum _umode_action +{ + umode_create, umode_destroy, umode_add, umode_delete +} umode_action_t; + +struct _umode +{ + struct _umode *next; + + umode_action_t action; + XRRModeInfo mode; + name_t output; + name_t name; +}; + +struct _provider +{ + name_t provider; + XRRProviderInfo *info; +}; + +struct _monitors +{ + int n; + XRRMonitorInfo *monitors; +}; + +struct _umonitor +{ + struct _umonitor *next; + char *name; + Bool set; + Bool primary; + int x, y, width, height; + int mmwidth, mmheight; + int noutput; + name_t *outputs; +}; + +#define OUTPUT_NAME 1 + +#define CRTC_OFF 2 +#define CRTC_UNSET 3 +#define CRTC_INDEX 0x40000000 + +#define MODE_NAME 1 +#define MODE_OFF 2 +#define MODE_UNSET 3 +#define MODE_PREF 4 + +#define POS_UNSET -1 + +static output_t *all_outputs = NULL; +static output_t **all_outputs_tail = &all_outputs; +static crtc_t *crtcs; +static int num_crtcs; +static XRRScreenResources *res; +static int fb_width = 0, fb_height = 0; +static int fb_width_mm = 0, fb_height_mm = 0; +static double dpi = 0; +static Bool dryrun = False; +static int minWidth, maxWidth, minHeight, maxHeight; +static Bool has_1_2 = False; +static Bool has_1_3 = False; +static Bool has_1_4 = False; +static Bool has_1_5 = False; +static monitors_t *monitors; + +static int mode_height(XRRModeInfo *mode_info, Rotation rotation) +{ + switch (rotation & 0xf) + { + case RR_Rotate_0: + case RR_Rotate_180: + return mode_info->height; + case RR_Rotate_90: + case RR_Rotate_270: + return mode_info->width; + default: + return 0; + } +} + +static int mode_width(XRRModeInfo *mode_info, Rotation rotation) +{ + switch (rotation & 0xf) + { + case RR_Rotate_0: + case RR_Rotate_180: + return mode_info->width; + case RR_Rotate_90: + case RR_Rotate_270: + return mode_info->height; + default: + return 0; + } +} + +static Bool transform_point(XTransform *transform, double *xp, double *yp) +{ + double vector[3]; + double result[3]; + int i, j; + double v; + + vector[0] = *xp; + vector[1] = *yp; + vector[2] = 1; + for (j = 0; j < 3; j++) + { + v = 0; + for (i = 0; i < 3; i++) + v += (XFixedToDouble(transform->matrix[j][i]) * vector[i]); + result[j] = v; + } + if (!result[2]) + return False; + for (j = 0; j < 2; j++) + { + vector[j] = result[j] / result[2]; + if (vector[j] > 32767 || vector[j] < -32767) + return False; + } + *xp = vector[0]; + *yp = vector[1]; + return True; +} + +static void path_bounds(XTransform *transform, point_t *points, int npoints, box_t *box) +{ + int i; + box_t point; + + for (i = 0; i < npoints; i++) + { + double x, y; + x = points[i].x; + y = points[i].y; + transform_point(transform, &x, &y); + point.x1 = floor(x); + point.y1 = floor(y); + point.x2 = ceil(x); + point.y2 = ceil(y); + if (i == 0) + *box = point; + else + { + if (point.x1 < box->x1) + box->x1 = point.x1; + if (point.y1 < box->y1) + box->y1 = point.y1; + if (point.x2 > box->x2) + box->x2 = point.x2; + if (point.y2 > box->y2) + box->y2 = point.y2; + } + } +} + +static void mode_geometry(XRRModeInfo *mode_info, Rotation rotation, XTransform *transform, box_t *bounds) +{ + point_t rect[4]; + int width = mode_width(mode_info, rotation); + int height = mode_height(mode_info, rotation); + + rect[0].x = 0; + rect[0].y = 0; + rect[1].x = width; + rect[1].y = 0; + rect[2].x = width; + rect[2].y = height; + rect[3].x = 0; + rect[3].y = height; + path_bounds(transform, rect, 4, bounds); +} + +static double mode_refresh(const XRRModeInfo *mode_info) +{ + double rate; + double vTotal = mode_info->vTotal; + + if (mode_info->modeFlags & RR_DoubleScan) + { + vTotal *= 2; + } + + if (mode_info->modeFlags & RR_Interlace) + { + vTotal /= 2; + } + + if (mode_info->hTotal && vTotal) + rate = ((double) mode_info->dotClock / ((double) mode_info->hTotal * (double) vTotal)); + else + rate = 0; + return rate; +} + +static void init_name(name_t *name) +{ + memset(name, 0, sizeof(*name)); + name->kind = name_none; +} + +static void set_name_string(name_t *name, char *string) +{ + name->kind |= name_string; + name->string = string; +} + +static void set_name_xid(name_t *name, XID xid) +{ + name->kind |= name_xid; + name->xid = xid; +} + +static void set_name_index(name_t *name, int idx) +{ + name->kind |= name_index; + name->index = idx; +} + +static void set_name_preferred(name_t *name) +{ + name->kind |= name_preferred; +} + +static void set_name_all(name_t *name, name_t *old) +{ + if (old->kind & name_xid) + name->xid = old->xid; + if (old->kind & name_string) + name->string = old->string; + if (old->kind & name_index) + name->index = old->index; + name->kind |= old->kind; +} + +static void set_name(name_t *name, char *string, name_kind_t valid) +{ + unsigned int xid; + int idx; + + if ((valid & name_xid) && sscanf(string, "0x%x", &xid) == 1) + set_name_xid(name, xid); + else if ((valid & name_index) && sscanf(string, "%d", &idx) == 1) + set_name_index(name, idx); + else if (valid & name_string) + set_name_string(name, string); + else + argerr("invalid name '%s'\n", string); +} + +static void init_transform(transform_t *transform) +{ + int x; + memset(&transform->transform, '\0', sizeof(transform->transform)); + for (x = 0; x < 3; x++) + transform->transform.matrix[x][x] = XDoubleToFixed(1.0); + transform->filter = ""; + transform->nparams = 0; + transform->params = NULL; +} + +static void set_transform(transform_t *dest, XTransform *transform, const char *filter, XFixed *params, int nparams) +{ + dest->transform = *transform; + dest->filter = strdup(filter); + dest->nparams = nparams; + dest->params = malloc(nparams * sizeof(XFixed)); + memcpy(dest->params, params, nparams * sizeof(XFixed)); +} + +static void copy_transform(transform_t *dest, transform_t *src) +{ + set_transform(dest, &src->transform, src->filter, src->params, src->nparams); +} + +static Bool equal_transform(transform_t *a, transform_t *b) +{ + if (memcmp(&a->transform, &b->transform, sizeof(XTransform)) != 0) + return False; + if (strcmp(a->filter, b->filter) != 0) + return False; + if (a->nparams != b->nparams) + return False; + if (memcmp(a->params, b->params, a->nparams * sizeof(XFixed)) != 0) + return False; + return True; +} + +static output_t* +add_output(void) +{ + output_t *output = calloc(1, sizeof(output_t)); + + if (!output) + fatal("out of memory\n"); + output->next = NULL; + output->found = False; + output->brightness = 1.0; + *all_outputs_tail = output; + all_outputs_tail = &output->next; + return output; +} + +static output_t* +find_output(name_t *name) +{ + output_t *output; + + for (output = all_outputs; output; output = output->next) + { + name_kind_t common = name->kind & output->output.kind; + + if ((common & name_xid) && name->xid == output->output.xid) + break; + if ((common & name_string) && !strcmp(name->string, output->output.string)) + break; + if ((common & name_index) && name->index == output->output.index) + break; + } + return output; +} + +static output_t* +find_output_by_xid(RROutput output) +{ + name_t output_name; + + init_name(&output_name); + set_name_xid(&output_name, output); + return find_output(&output_name); +} + +static output_t* +find_output_by_name(char *name) +{ + name_t output_name; + + init_name(&output_name); + set_name_string(&output_name, name); + return find_output(&output_name); +} + +static crtc_t* +find_crtc(name_t *name) +{ + int c; + crtc_t *crtc = NULL; + + for (c = 0; c < num_crtcs; c++) + { + name_kind_t common; + + crtc = &crtcs[c]; + common = name->kind & crtc->crtc.kind; + + if ((common & name_xid) && name->xid == crtc->crtc.xid) + break; + if ((common & name_string) && !strcmp(name->string, crtc->crtc.string)) + break; + if ((common & name_index) && name->index == crtc->crtc.index) + break; + crtc = NULL; + } + return crtc; +} + +static crtc_t* +find_crtc_by_xid(RRCrtc crtc) +{ + name_t crtc_name; + + init_name(&crtc_name); + set_name_xid(&crtc_name, crtc); + return find_crtc(&crtc_name); +} + +static XRRModeInfo* +find_mode(name_t *name, double refresh) +{ + int m; + XRRModeInfo *best = NULL; + double bestDist = 0; + + for (m = 0; m < res->nmode; m++) + { + XRRModeInfo *mode = &res->modes[m]; + if ((name->kind & name_xid) && name->xid == mode->id) + { + best = mode; + break; + } + if ((name->kind & name_string) && !strcmp(name->string, mode->name)) + { + double dist; + + if (refresh) + dist = fabs(mode_refresh(mode) - refresh); + else + dist = 0; + if (!best || dist < bestDist) + { + bestDist = dist; + best = mode; + } + } + } + return best; +} + +static XRRModeInfo* +find_mode_by_xid(RRMode mode) +{ + name_t mode_name; + + init_name(&mode_name); + set_name_xid(&mode_name, mode); + return find_mode(&mode_name, 0); +} + +static XRRModeInfo* +find_mode_for_output(output_t *output, name_t *name) +{ + XRROutputInfo *output_info = output->output_info; + int m; + XRRModeInfo *best = NULL; + double bestDist = 0; + + for (m = 0; m < output_info->nmode; m++) + { + XRRModeInfo *mode; + + mode = find_mode_by_xid(output_info->modes[m]); + if (!mode) + continue; + if ((name->kind & name_xid) && name->xid == mode->id) + { + best = mode; + break; + } + if ((name->kind & name_string) && !strcmp(name->string, mode->name)) + { + double dist; + + if (!output->refresh && (mode->modeFlags & RR_DoubleScan)) + continue; + + if (output->refresh) + dist = fabs(mode_refresh(mode) - output->refresh); + else + dist = 0; + if (!best || dist < bestDist) + { + bestDist = dist; + best = mode; + } + } + } + return best; +} + +static XRRModeInfo* +preferred_mode(output_t *output) +{ + XRROutputInfo *output_info = output->output_info; + int m; + XRRModeInfo *best; + int bestDist; + + best = NULL; + bestDist = 0; + for (m = 0; m < output_info->nmode; m++) + { + XRRModeInfo *mode_info = find_mode_by_xid(output_info->modes[m]); + int dist; + + if (m < output_info->npreferred) + dist = 0; + else if (output_info->mm_height) + dist = (1000 * DisplayHeight(dpy, screen) / DisplayHeightMM(dpy, screen) - 1000 * mode_info->height / output_info->mm_height); + else + dist = DisplayHeight(dpy, screen) - mode_info->height; + + if (dist < 0) + dist = -dist; + if (!best || dist < bestDist) + { + best = mode_info; + bestDist = dist; + } + } + return best; +} + +static Bool output_can_use_crtc(output_t *output, crtc_t *crtc) +{ + XRROutputInfo *output_info = output->output_info; + int c; + + for (c = 0; c < output_info->ncrtc; c++) + if (output_info->crtcs[c] == crtc->crtc.xid) + return True; + return False; +} + +static Bool output_can_use_mode(output_t *output, XRRModeInfo *mode) +{ + XRROutputInfo *output_info = output->output_info; + int m; + + for (m = 0; m < output_info->nmode; m++) + if (output_info->modes[m] == mode->id) + return True; + return False; +} + +static Bool crtc_can_use_rotation(crtc_t *crtc, Rotation rotation) +{ + Rotation rotations = crtc->crtc_info->rotations; + Rotation dir = rotation & (RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270); + Rotation reflect = rotation & (RR_Reflect_X | RR_Reflect_Y); + if (((rotations & dir) != 0) && ((rotations & reflect) == reflect)) + return True; + return False; +} + +static Bool output_can_use_rotation(output_t *output, Rotation rotation) +{ + XRROutputInfo *output_info = output->output_info; + int c; + + for (c = 0; c < output_info->ncrtc; c++) + { + crtc_t *crtc = find_crtc_by_xid(output_info->crtcs[c]); + if (crtc && !crtc_can_use_rotation(crtc, rotation)) + return False; + } + return True; +} + +static Bool output_is_primary(output_t *output) +{ + if (has_1_3) + return XRRGetOutputPrimary(dpy, root) == output->output.xid; + return False; +} + +static int find_last_non_clamped(CARD16 array[], int size) +{ + int i; + for (i = size - 1; i > 0; i--) + { + if (array[i] < 0xffff) + return i; + } + return 0; +} + +static void set_gamma_info(output_t *output) +{ + XRRCrtcGamma *crtc_gamma; + double i1, v1, i2, v2; + int size, middle, last_best, last_red, last_green, last_blue; + CARD16 *best_array; + + if (!output->crtc_info) + return; + + size = XRRGetCrtcGammaSize(dpy, output->crtc_info->crtc.xid); + if (!size) + { + warning("Failed to get size of gamma for output %s\n", output->output.string); + return; + } + + crtc_gamma = XRRGetCrtcGamma(dpy, output->crtc_info->crtc.xid); + if (!crtc_gamma) + { + warning("Failed to get gamma for output %s\n", output->output.string); + return; + } + + last_red = find_last_non_clamped(crtc_gamma->red, size); + last_green = find_last_non_clamped(crtc_gamma->green, size); + last_blue = find_last_non_clamped(crtc_gamma->blue, size); + best_array = crtc_gamma->red; + last_best = last_red; + if (last_green > last_best) + { + last_best = last_green; + best_array = crtc_gamma->green; + } + if (last_blue > last_best) + { + last_best = last_blue; + best_array = crtc_gamma->blue; + } + if (last_best == 0) + last_best = 1; + + middle = last_best / 2; + i1 = (double) (middle + 1) / size; + v1 = (double) (best_array[middle]) / 65535; + i2 = (double) (last_best + 1) / size; + v2 = (double) (best_array[last_best]) / 65535; + if (v2 < 0.0001) + { + output->brightness = 0; + output->gamma.red = 1; + output->gamma.green = 1; + output->gamma.blue = 1; + } + else + { + if ((last_best + 1) == size) + output->brightness = v2; + else + output->brightness = exp((log(v2) * log(i1) - log(v1) * log(i2)) / log(i1 / i2)); + output->gamma.red = log((double) (crtc_gamma->red[last_red / 2]) / output->brightness / 65535) / log((double) ((last_red / 2) + 1) / size); + output->gamma.green = log((double) (crtc_gamma->green[last_green / 2]) / output->brightness / 65535) + / log((double) ((last_green / 2) + 1) / size); + output->gamma.blue = log((double) (crtc_gamma->blue[last_blue / 2]) / output->brightness / 65535) + / log((double) ((last_blue / 2) + 1) / size); + } + + XRRFreeGamma(crtc_gamma); +} + +static void set_output_info(output_t *output, RROutput xid, XRROutputInfo *output_info) +{ + if (output_info->connection != RR_Disconnected && !output_info->nmode) + warning("Output %s is not disconnected but has no modes\n", output_info->name); + + if (!(output->output.kind & name_xid)) + set_name_xid(&output->output, xid); + if (!(output->output.kind & name_string)) + set_name_string(&output->output, output_info->name); + output->output_info = output_info; + + if (!(output->changes & changes_crtc)) + set_name_xid(&output->crtc, output_info->crtc); + + if (output->crtc.kind == name_xid && output->crtc.xid == None) + output->crtc_info = NULL; + else + { + output->crtc_info = find_crtc(&output->crtc); + if (!output->crtc_info) + { + if (output->crtc.kind & name_xid) + fatal("cannot find crtc 0x%lx\n", output->crtc.xid); + if (output->crtc.kind & name_index) + fatal("cannot find crtc %d\n", output->crtc.index); + } + if (!output_can_use_crtc(output, output->crtc_info)) + fatal("output %s cannot use crtc 0x%lx\n", output->output.string, output->crtc_info->crtc.xid); + } + + if (!(output->changes & changes_mode)) + { + crtc_t *crtc = NULL; + + if (output_info->crtc) + crtc = find_crtc_by_xid(output_info->crtc); + if (crtc && crtc->crtc_info) + set_name_xid(&output->mode, crtc->crtc_info->mode); + else if (output->crtc_info) + set_name_xid(&output->mode, output->crtc_info->crtc_info->mode); + else + set_name_xid(&output->mode, None); + if (output->mode.xid) + { + output->mode_info = find_mode_by_xid(output->mode.xid); + if (!output->mode_info) + fatal("server did not report mode 0x%lx for output %s\n", output->mode.xid, output->output.string); + } + else + output->mode_info = NULL; + } + else if (output->mode.kind == name_xid && output->mode.xid == None) + output->mode_info = NULL; + else + { + if (output->mode.kind == name_preferred) + output->mode_info = preferred_mode(output); + else + output->mode_info = find_mode_for_output(output, &output->mode); + if (!output->mode_info) + { + if (output->mode.kind & name_preferred) + fatal("cannot find preferred mode\n"); + if (output->mode.kind & name_string) + fatal("cannot find mode %s\n", output->mode.string); + if (output->mode.kind & name_xid) + fatal("cannot find mode 0x%lx\n", output->mode.xid); + } + if (!output_can_use_mode(output, output->mode_info)) + fatal("output %s cannot use mode %s\n", output->output.string, output->mode_info->name); + } + + if (!(output->changes & changes_position)) + { + if (output->crtc_info) + { + output->x = output->crtc_info->crtc_info->x; + output->y = output->crtc_info->crtc_info->y; + } + else + { + output->x = 0; + output->y = 0; + } + } + + if (!(output->changes & changes_rotation)) + { + output->rotation &= ~0xf; + if (output->crtc_info) + output->rotation |= (output->crtc_info->crtc_info->rotation & 0xf); + else + output->rotation = RR_Rotate_0; + } + if (!(output->changes & changes_reflection)) + { + output->rotation &= ~(RR_Reflect_X | RR_Reflect_Y); + if (output->crtc_info) + output->rotation |= (output->crtc_info->crtc_info->rotation & (RR_Reflect_X | RR_Reflect_Y)); + } + if (!output_can_use_rotation(output, output->rotation)) + fatal("output %s cannot use rotation \"%s\" reflection \"%s\"\n", output->output.string, rotation_name(output->rotation), + reflection_name(output->rotation)); + + if (!(output->changes & changes_gamma)) + set_gamma_info(output); + + if (!(output->changes & changes_transform)) + { + if (output->crtc_info) + copy_transform(&output->transform, &output->crtc_info->current_transform); + else + init_transform(&output->transform); + } + else + { + if (output->scale_from_w > 0 && output->mode_info) + { + double sx = (double) output->scale_from_w / output->mode_info->width; + double sy = (double) output->scale_from_h / output->mode_info->height; + if (verbose) + printf("scaling %s by %lfx%lf\n", output->output.string, sx, sy); + init_transform(&output->transform); + output->transform.transform.matrix[0][0] = XDoubleToFixed(sx); + output->transform.transform.matrix[1][1] = XDoubleToFixed(sy); + output->transform.transform.matrix[2][2] = XDoubleToFixed(1.0); + if (sx != 1 || sy != 1) + output->transform.filter = "bilinear"; + else + output->transform.filter = "nearest"; + output->transform.nparams = 0; + output->transform.params = NULL; + } + } + if (output->changes & changes_filter) + { + output->transform.filter = filter_names[filter_type]; + } + + if (!(output->changes & changes_primary)) + output->primary = output_is_primary(output); +} + +static void get_screen(Bool current) +{ + if (!has_1_2) + fatal("Server RandR version before 1.2\n"); + + if (res) + return; + + XRRGetScreenSizeRange(dpy, root, &minWidth, &minHeight, &maxWidth, &maxHeight); + + if (current) + res = XRRGetScreenResourcesCurrent(dpy, root); + else + res = XRRGetScreenResources(dpy, root); + if (!res) + fatal("could not get screen resources"); +} + +static void get_crtcs(void) +{ + int c; + + num_crtcs = res->ncrtc; + crtcs = calloc(num_crtcs, sizeof(crtc_t)); + if (!crtcs) + fatal("out of memory\n"); + + for (c = 0; c < res->ncrtc; c++) + { + XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(dpy, res, res->crtcs[c]); + XRRCrtcTransformAttributes *attr; + XRRPanning *panning_info = NULL; + + if (has_1_3) + { + XRRPanning zero; + memset(&zero, 0, sizeof(zero)); + panning_info = XRRGetPanning(dpy, res, res->crtcs[c]); + zero.timestamp = panning_info->timestamp; + if (!memcmp(panning_info, &zero, sizeof(zero))) + { + Xfree(panning_info); + panning_info = NULL; + } + } + + set_name_xid(&crtcs[c].crtc, res->crtcs[c]); + set_name_index(&crtcs[c].crtc, c); + if (!crtc_info) + fatal("could not get crtc 0x%lx information\n", res->crtcs[c]); + crtcs[c].crtc_info = crtc_info; + crtcs[c].panning_info = panning_info; + if (crtc_info->mode == None) + { + crtcs[c].mode_info = NULL; + crtcs[c].x = 0; + crtcs[c].y = 0; + crtcs[c].rotation = RR_Rotate_0; + } + if (XRRGetCrtcTransform(dpy, res->crtcs[c], &attr) && attr) + { + set_transform(&crtcs[c].current_transform, &attr->currentTransform, attr->currentFilter, attr->currentParams, attr->currentNparams); + XFree(attr); + } + else + { + init_transform(&crtcs[c].current_transform); + } + copy_transform(&crtcs[c].pending_transform, &crtcs[c].current_transform); + } +} + +static void crtc_add_output(crtc_t *crtc, output_t *output) +{ + if (crtc->outputs) + crtc->outputs = realloc(crtc->outputs, (crtc->noutput + 1) * sizeof(output_t*)); + else + { + crtc->outputs = malloc(sizeof(output_t*)); + crtc->x = output->x; + crtc->y = output->y; + crtc->rotation = output->rotation; + crtc->mode_info = output->mode_info; + copy_transform(&crtc->pending_transform, &output->transform); + } + if (!crtc->outputs) + fatal("out of memory\n"); + crtc->outputs[crtc->noutput++] = output; +} + +static void set_crtcs(void) +{ + output_t *output; + + for (output = all_outputs; output; output = output->next) + { + if (!output->mode_info) + continue; + crtc_add_output(output->crtc_info, output); + } +} + +static void set_panning(void) +{ + output_t *output; + + for (output = all_outputs; output; output = output->next) + { + if (!output->crtc_info) + continue; + if (!(output->changes & changes_panning)) + continue; + if (!output->crtc_info->panning_info) + output->crtc_info->panning_info = malloc(sizeof(XRRPanning)); + memcpy(output->crtc_info->panning_info, &output->panning, sizeof(XRRPanning)); + output->crtc_info->changing = 1; + } +} + +static void set_gamma(void) +{ + output_t *output; + + for (output = all_outputs; output; output = output->next) + { + int i, size; + crtc_t *crtc; + XRRCrtcGamma *crtc_gamma; + float gammaRed; + float gammaGreen; + float gammaBlue; + + if (!(output->changes & changes_gamma)) + continue; + + if (!output->crtc_info) + { + fatal("Need crtc to set gamma on.\n"); + continue; + } + + crtc = output->crtc_info; + + size = XRRGetCrtcGammaSize(dpy, crtc->crtc.xid); + + if (!size) + { + fatal("Gamma size is 0.\n"); + continue; + } + + if (size > 65536) + { + fatal("Gamma correction table is impossibly large.\n"); + continue; + } + + crtc_gamma = XRRAllocGamma(size); + if (!crtc_gamma) + { + fatal("Gamma allocation failed.\n"); + continue; + } + + if (output->gamma.red == 0.0) + output->gamma.red = 1.0; + if (output->gamma.green == 0.0) + output->gamma.green = 1.0; + if (output->gamma.blue == 0.0) + output->gamma.blue = 1.0; + + gammaRed = 1.0 / output->gamma.red; + gammaGreen = 1.0 / output->gamma.green; + gammaBlue = 1.0 / output->gamma.blue; + + for (i = 0; i < size; i++) + { + if (gammaRed == 1.0 && output->brightness == 1.0) + crtc_gamma->red[i] = (double) i / (double) (size - 1) * 65535.0; + else + crtc_gamma->red[i] = dmin(pow((double) i / (double) (size - 1), gammaRed) * output->brightness, 1.0) * 65535.0; + + if (gammaGreen == 1.0 && output->brightness == 1.0) + crtc_gamma->green[i] = (double) i / (double) (size - 1) * 65535.0; + else + crtc_gamma->green[i] = dmin(pow((double) i / (double) (size - 1), gammaGreen) * output->brightness, 1.0) * 65535.0; + + if (gammaBlue == 1.0 && output->brightness == 1.0) + crtc_gamma->blue[i] = (double) i / (double) (size - 1) * 65535.0; + else + crtc_gamma->blue[i] = dmin(pow((double) i / (double) (size - 1), gammaBlue) * output->brightness, 1.0) * 65535.0; + } + + XRRSetCrtcGamma(dpy, crtc->crtc.xid, crtc_gamma); + + free(crtc_gamma); + } +} + +static void set_primary(void) +{ + output_t *output; + + if (no_primary) + { + XRRSetOutputPrimary(dpy, root, None); + } + else + { + for (output = all_outputs; output; output = output->next) + { + if (!(output->changes & changes_primary)) + continue; + if (output->primary) + XRRSetOutputPrimary(dpy, root, output->output.xid); + } + } +} + +static Status crtc_disable(crtc_t *crtc) +{ + if (verbose) + printf("crtc %d: disable\n", crtc->crtc.index); + + if (dryrun) + return RRSetConfigSuccess; + return XRRSetCrtcConfig(dpy, res, crtc->crtc.xid, CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0); +} + +static void crtc_set_transform(crtc_t *crtc, transform_t *transform) +{ + int major, minor; + + XRRQueryVersion(dpy, &major, &minor); + if (major > 1 || (major == 1 && minor >= 3)) + XRRSetCrtcTransform(dpy, crtc->crtc.xid, &transform->transform, transform->filter, transform->params, transform->nparams); +} + +static Status crtc_revert(crtc_t *crtc) +{ + XRRCrtcInfo *crtc_info = crtc->crtc_info; + + if (verbose) + printf("crtc %d: revert\n", crtc->crtc.index); + + if (dryrun) + return RRSetConfigSuccess; + + if (!equal_transform(&crtc->current_transform, &crtc->pending_transform)) + crtc_set_transform(crtc, &crtc->current_transform); + return XRRSetCrtcConfig(dpy, res, crtc->crtc.xid, CurrentTime, crtc_info->x, crtc_info->y, crtc_info->mode, crtc_info->rotation, + crtc_info->outputs, crtc_info->noutput); +} + +static Status crtc_apply(crtc_t *crtc) +{ + RROutput *rr_outputs; + int o; + Status s; + RRMode mode = None; + + if (!crtc->changing || !crtc->mode_info) + return RRSetConfigSuccess; + + rr_outputs = calloc(crtc->noutput, sizeof(RROutput)); + if (!rr_outputs) + return BadAlloc; + for (o = 0; o < crtc->noutput; o++) + rr_outputs[o] = crtc->outputs[o]->output.xid; + mode = crtc->mode_info->id; + if (verbose) + { + printf("crtc %d: %12s %6.2f +%d+%d", crtc->crtc.index, crtc->mode_info->name, mode_refresh(crtc->mode_info), crtc->x, crtc->y); + for (o = 0; o < crtc->noutput; o++) + printf(" \"%s\"", crtc->outputs[o]->output.string); + printf("\n"); + } + + if (dryrun) + s = RRSetConfigSuccess; + else + { + if (!equal_transform(&crtc->current_transform, &crtc->pending_transform)) + crtc_set_transform(crtc, &crtc->pending_transform); + s = XRRSetCrtcConfig(dpy, res, crtc->crtc.xid, CurrentTime, crtc->x, crtc->y, mode, crtc->rotation, rr_outputs, crtc->noutput); + if (s == RRSetConfigSuccess && crtc->panning_info) + { + if (has_1_3) + s = XRRSetPanning(dpy, res, crtc->crtc.xid, crtc->panning_info); + else + fatal("panning needs RandR 1.3\n"); + } + } + free(rr_outputs); + return s; +} + +static void screen_revert(void) +{ + if (verbose) + printf("screen %d: revert\n", screen); + + if (dryrun) + return; + XRRSetScreenSize(dpy, root, DisplayWidth(dpy, screen), DisplayHeight(dpy, screen), DisplayWidthMM(dpy, screen), DisplayHeightMM(dpy, screen)); +} + +static void screen_apply(void) +{ + if (fb_width == DisplayWidth(dpy, screen) && fb_height == DisplayHeight(dpy, screen) && fb_width_mm == DisplayWidthMM(dpy, screen) + && fb_height_mm == DisplayHeightMM(dpy, screen)) + { + return; + } + if (verbose) + printf("screen %d: %dx%d %dx%d mm %6.2fdpi\n", screen, fb_width, fb_height, fb_width_mm, fb_height_mm, dpi); + if (dryrun) + return; + XRRSetScreenSize(dpy, root, fb_width, fb_height, fb_width_mm, fb_height_mm); +} + +static void revert(void) +{ + int c; + + for (c = 0; c < res->ncrtc; c++) + crtc_disable(&crtcs[c]); + screen_revert(); + for (c = 0; c < res->ncrtc; c++) + crtc_revert(&crtcs[c]); +} + +static void _X_NORETURN panic(Status s, crtc_t *crtc) +{ + int c = crtc->crtc.index; + const char *message; + + switch (s) + { + case RRSetConfigSuccess: + message = "succeeded"; + break; + case BadAlloc: + message = "out of memory"; + break; + case RRSetConfigFailed: + message = "failed"; + break; + case RRSetConfigInvalidConfigTime: + message = "invalid config time"; + break; + case RRSetConfigInvalidTime: + message = "invalid time"; + break; + default: + message = "unknown failure"; + break; + } + + fprintf(stderr, "%s: Configure crtc %d %s\n", program_name, c, message); + revert(); + exit(1); +} + +static void apply(void) +{ + Status s; + int c; + + if (grab_server) + XGrabServer(dpy); + + for (c = 0; c < res->ncrtc; c++) + { + crtc_t *crtc = &crtcs[c]; + XRRCrtcInfo *crtc_info = crtc->crtc_info; + + if (crtc_info->mode == None) + continue; + + if (crtc->mode_info) + { + XRRModeInfo *old_mode = find_mode_by_xid(crtc_info->mode); + int x, y, w, h; + box_t bounds; + + if (!old_mode) + panic(RRSetConfigFailed, crtc); + + mode_geometry(old_mode, crtc_info->rotation, &crtc->current_transform.transform, &bounds); + + x = crtc_info->x + bounds.x1; + y = crtc_info->y + bounds.y1; + w = bounds.x2 - bounds.x1; + h = bounds.y2 - bounds.y1; + + if (x + w <= fb_width && y + h <= fb_height) + continue; + crtc->changing = True; + } + s = crtc_disable(crtc); + if (s != RRSetConfigSuccess) + panic(s, crtc); + } + + screen_apply(); + + for (c = 0; c < res->ncrtc; c++) + { + crtc_t *crtc = &crtcs[c]; + + s = crtc_apply(crtc); + if (s != RRSetConfigSuccess) + panic(s, crtc); + } + + set_primary(); + + if (grab_server) + XUngrabServer(dpy); +} + +static void get_outputs(void) +{ + int o; + output_t *q; + + for (o = 0; o < res->noutput; o++) + { + XRROutputInfo *output_info = XRRGetOutputInfo(dpy, res, res->outputs[o]); + output_t *output; + name_t output_name; + if (!output_info) + fatal("could not get output 0x%lx information\n", res->outputs[o]); + init_name(&output_name); + set_name_xid(&output_name, res->outputs[o]); + set_name_index(&output_name, o); + set_name_string(&output_name, output_info->name); + output = find_output(&output_name); + if (!output) + { + output = add_output(); + set_name_all(&output->output, &output_name); + + if (automatic) + { + switch (output_info->connection) + { + case RR_Connected: + if (!output_info->crtc) + { + output->changes |= changes_automatic; + output->automatic = True; + } + break; + case RR_Disconnected: + if (output_info->crtc) + { + output->changes |= changes_automatic; + output->automatic = True; + } + break; + } + } + } + output->found = True; + + if (output->automatic) + { + switch (output_info->connection) + { + case RR_Connected: + case RR_UnknownConnection: + if ((!(output->changes & changes_mode))) + { + set_name_preferred(&output->mode); + output->changes |= changes_mode; + } + break; + case RR_Disconnected: + if ((!(output->changes & changes_mode))) + { + set_name_xid(&output->mode, None); + set_name_xid(&output->crtc, None); + output->changes |= changes_mode; + output->changes |= changes_crtc; + } + break; + } + } + + set_output_info(output, res->outputs[o], output_info); + } + for (q = all_outputs; q; q = q->next) + { + if (!q->found) + { + fprintf(stderr, "warning: output %s not found; ignoring\n", q->output.string); + } + } +} + +static void get_monitors(Bool get_active) +{ + XRRMonitorInfo *m; + int n; + + if (!has_1_5 || monitors) + return; + + m = XRRGetMonitors(dpy, root, get_active, &n); + if (n == -1) + fatal("get monitors failed\n"); + monitors = calloc(1, sizeof(monitors_t)); + monitors->n = n; + monitors->monitors = m; +} + +static void mark_changing_crtcs(void) +{ + int c; + + for (c = 0; c < num_crtcs; c++) + { + crtc_t *crtc = &crtcs[c]; + int o; + output_t *output; + + for (o = 0; o < crtc->crtc_info->noutput; o++) + { + output = find_output_by_xid(crtc->crtc_info->outputs[o]); + if (!output) + fatal("cannot find output 0x%lx\n", crtc->crtc_info->outputs[o]); + if (output->changes) + crtc->changing = True; + } + + for (o = 0; o < crtc->noutput; o++) + { + output = crtc->outputs[o]; + if (output->changes) + crtc->changing = True; + } + } +} + +static Bool check_crtc_for_output(crtc_t *crtc, output_t *output) +{ + int c; + int l; + output_t *other; + + for (c = 0; c < output->output_info->ncrtc; c++) + if (output->output_info->crtcs[c] == crtc->crtc.xid) + break; + if (c == output->output_info->ncrtc) + return False; + for (other = all_outputs; other; other = other->next) + { + if (other == output) + continue; + + if (other->mode_info == NULL) + continue; + + if (other->crtc_info != crtc) + continue; + + for (l = 0; l < output->output_info->nclone; l++) + if (output->output_info->clones[l] == other->output.xid) + break; + if (l == output->output_info->nclone) + return False; + } + + if (crtc->noutput) + { + if (crtc->mode_info != output->mode_info) + return False; + if (crtc->x != output->x) + return False; + if (crtc->y != output->y) + return False; + if (crtc->rotation != output->rotation) + return False; + if (!equal_transform(&crtc->current_transform, &output->transform)) + return False; + } + else if (crtc->crtc_info->noutput) + { + XRRModeInfo *mode = find_mode_by_xid(crtc->crtc_info->mode); + + if (mode != output->mode_info) + return False; + if (crtc->crtc_info->x != output->x) + return False; + if (crtc->crtc_info->y != output->y) + return False; + if (crtc->crtc_info->rotation != output->rotation) + return False; + } + return True; +} + +static crtc_t* +find_crtc_for_output(output_t *output) +{ + int c; + + for (c = 0; c < output->output_info->ncrtc; c++) + { + crtc_t *crtc; + + crtc = find_crtc_by_xid(output->output_info->crtcs[c]); + if (!crtc) + fatal("cannot find crtc 0x%lx\n", output->output_info->crtcs[c]); + + if (check_crtc_for_output(crtc, output)) + return crtc; + } + return NULL; +} + +static void set_positions(void) +{ + output_t *output; + Bool keep_going; + Bool any_set; + int min_x, min_y; + + for (;;) + { + any_set = False; + keep_going = False; + for (output = all_outputs; output; output = output->next) + { + output_t *relation; + name_t relation_name; + + if (!(output->changes & changes_relation)) + continue; + + if (output->mode_info == NULL) + continue; + + init_name(&relation_name); + set_name_string(&relation_name, output->relative_to); + relation = find_output(&relation_name); + if (!relation) + fatal("cannot find output \"%s\"\n", output->relative_to); + + if (relation->mode_info == NULL) + { + output->x = 0; + output->y = 0; + output->changes |= changes_position; + any_set = True; + continue; + } + + if ((relation->changes & changes_relation) && !(relation->changes & changes_position)) + { + keep_going = True; + continue; + } + + switch (output->relation) + { + case relation_left_of: + output->y = relation->y; + output->x = relation->x - mode_width(output->mode_info, output->rotation); + break; + case relation_right_of: + output->y = relation->y; + output->x = relation->x + mode_width(relation->mode_info, relation->rotation); + break; + case relation_above: + output->x = relation->x; + output->y = relation->y - mode_height(output->mode_info, output->rotation); + break; + case relation_below: + output->x = relation->x; + output->y = relation->y + mode_height(relation->mode_info, relation->rotation); + break; + case relation_same_as: + output->x = relation->x; + output->y = relation->y; + } + output->changes |= changes_position; + any_set = True; + } + if (!keep_going) + break; + if (!any_set) + fatal("loop in relative position specifications\n"); + } + + min_x = 32768; + min_y = 32768; + for (output = all_outputs; output; output = output->next) + { + if (output->mode_info == NULL) + continue; + + if (output->x < min_x) + min_x = output->x; + if (output->y < min_y) + min_y = output->y; + } + if (min_x || min_y) + { + for (output = all_outputs; output; output = output->next) + { + if (output->mode_info == NULL) + continue; + + output->x -= min_x; + output->y -= min_y; + output->changes |= changes_position; + } + } +} + +static void set_screen_size(void) +{ + output_t *output; + Bool fb_specified = fb_width != 0 && fb_height != 0; + + for (output = all_outputs; output; output = output->next) + { + XRRModeInfo *mode_info = output->mode_info; + int x, y, w, h; + box_t bounds; + + if (!mode_info) + continue; + + mode_geometry(mode_info, output->rotation, &output->transform.transform, &bounds); + x = output->x + bounds.x1; + y = output->y + bounds.y1; + w = bounds.x2 - bounds.x1; + h = bounds.y2 - bounds.y1; + + if (fb_specified) + { + if (x + w > fb_width || y + h > fb_height) + warning("specified screen %dx%d not large enough for output %s (%dx%d+%d+%d)\n", fb_width, fb_height, output->output.string, w, h, x, + y); + } + + else + { + XRRPanning *pan; + if (x + w > fb_width) + fb_width = x + w; + if (y + h > fb_height) + fb_height = y + h; + if (output->changes & changes_panning) + pan = &output->panning; + else + pan = output->crtc_info ? output->crtc_info->panning_info : NULL; + if (pan && pan->left + pan->width > fb_width) + fb_width = pan->left + pan->width; + if (pan && pan->top + pan->height > fb_height) + fb_height = pan->top + pan->height; + } + } + + if (fb_width > maxWidth || fb_height > maxHeight) + fatal("screen cannot be larger than %dx%d (desired size %dx%d)\n", maxWidth, maxHeight, fb_width, fb_height); + if (fb_specified) + { + if (fb_width < minWidth || fb_height < minHeight) + fatal("screen must be at least %dx%d\n", minWidth, minHeight); + } + else + { + if (fb_width < minWidth) + fb_width = minWidth; + if (fb_height < minHeight) + fb_height = minHeight; + } +} + +static void disable_outputs(output_t *outputs) +{ + while (outputs) + { + outputs->crtc_info = NULL; + outputs = outputs->next; + } +} + +static int pick_crtcs_score(output_t *outputs) +{ + output_t *output; + int best_score; + int my_score; + int score; + crtc_t *best_crtc; + int c; + + if (!outputs) + return 0; + + output = outputs; + outputs = outputs->next; + + output->crtc_info = NULL; + best_score = pick_crtcs_score(outputs); + if (output->mode_info == NULL) + return best_score; + + best_crtc = NULL; + + for (c = 0; c < output->output_info->ncrtc; c++) + { + crtc_t *crtc; + + crtc = find_crtc_by_xid(output->output_info->crtcs[c]); + if (!crtc) + fatal("cannot find crtc 0x%lx\n", output->output_info->crtcs[c]); + + disable_outputs(outputs); + if (!check_crtc_for_output(crtc, output)) + continue; + + my_score = 1000; + + if (crtc == output->current_crtc_info) + my_score++; + + output->crtc_info = crtc; + score = my_score + pick_crtcs_score(outputs); + if (score > best_score) + { + best_crtc = crtc; + best_score = score; + } + } + if (output->crtc_info != best_crtc) + output->crtc_info = best_crtc; + + (void) pick_crtcs_score(outputs); + + return best_score; +} + +static void pick_crtcs(void) +{ + output_t *output; + int saved_crtc_noutput[num_crtcs]; + int n; + + for (output = all_outputs; output; output = output->next) + { + if (output->changes && output->mode_info) + { + if (output->crtc_info) + { + if (output->crtc_info->crtc_info->noutput > 0 + && (output->crtc_info->crtc_info->noutput > 1 || output != find_output_by_xid(output->crtc_info->crtc_info->outputs[0]))) + break; + } + else + { + output->crtc_info = find_crtc_for_output(output); + if (!output->crtc_info) + break; + } + } + } + + if (!output) + return; + + for (output = all_outputs; output; output = output->next) + output->current_crtc_info = output->crtc_info; + + for (n = 0; n < num_crtcs; n++) + { + saved_crtc_noutput[n] = crtcs[n].crtc_info->noutput; + crtcs[n].crtc_info->noutput = 0; + } + + pick_crtcs_score(all_outputs); + + for (n = 0; n < num_crtcs; n++) + crtcs[n].crtc_info->noutput = saved_crtc_noutput[n]; + + for (output = all_outputs; output; output = output->next) + { + if (output->mode_info && !output->crtc_info) + fatal("cannot find crtc for output %s\n", output->output.string); + if (!output->changes && output->crtc_info != output->current_crtc_info) + output->changes |= changes_crtc; + } +} + +void set_default_global() +{ + fb_width_mm = 0; + fb_height_mm = 0; + all_outputs = NULL; + all_outputs_tail = &all_outputs; + crtcs = NULL; + num_crtcs = 0; + res = NULL; + fb_width = 0, fb_height = 0; + fb_width_mm = 0, fb_height_mm = 0; + dpi = 0; + dryrun = False; + has_1_2 = False; + has_1_3 = False; + has_1_4 = False; + has_1_5 = False; + program_name = NULL; + dpy = NULL; + screen = -1; + verbose = False; + automatic = False; + grab_server = True; + no_primary = False; + filter_type = -1; + monitors = NULL; +} + +typedef struct +{ + char *first_monitor; + char *second_monitor; + int is_position; + relation_t position; + int primary; +} x_change; + +int XChange(x_change *settings) +{ + if (!settings->first_monitor) + return 1; + + setenv("DISPLAY", ":0.0", 0); + + char *display_name = NULL; + int event_base, error_base; + output_t *config_output = NULL; + int major, minor; + + config_output = find_output_by_name(settings->first_monitor); + if (!config_output) + { + config_output = add_output(); + set_name(&config_output->output, settings->first_monitor, name_string | name_xid); + } + + if (settings->primary) + { + config_output->changes |= changes_primary; + config_output->primary = True; + } + + if (settings->is_position && settings->second_monitor) + { + config_output->relation = settings->position; + config_output->relative_to = settings->second_monitor; + config_output->changes |= changes_relation; + } + + dpy = XOpenDisplay(display_name); + + if (dpy == NULL) + { + fprintf(stderr, "Can't open display %s\n", XDisplayName(display_name)); + exit(1); + } + if (screen < 0) + screen = DefaultScreen(dpy); + if (screen >= ScreenCount(dpy)) + { + fprintf(stderr, "Invalid screen number %d (display has %d)\n", screen, ScreenCount(dpy)); + exit(1); + } + + root = RootWindow(dpy, screen); + + if (!XRRQueryExtension(dpy, &event_base, &error_base) || !XRRQueryVersion(dpy, &major, &minor)) + { + fprintf(stderr, "RandR extension missing\n"); + exit(1); + } + if (major > 1 || (major == 1 && minor >= 2)) + has_1_2 = True; + if (major > 1 || (major == 1 && minor >= 3)) + has_1_3 = True; + if (major > 1 || (major == 1 && minor >= 4)) + has_1_4 = True; + if (major > 1 || (major == 1 && minor >= 5)) + has_1_5 = True; + + get_screen(True); + get_crtcs(); + get_outputs(); + set_positions(); + set_screen_size(); + + pick_crtcs(); + set_crtcs(); + mark_changing_crtcs(); + + if (fb_width_mm == 0 || fb_height_mm == 0) + { + if (fb_width != DisplayWidth(dpy, screen) || fb_height != DisplayHeight(dpy, screen) || dpi != 0.0) + { + if (dpi <= 0) + dpi = (25.4 * DisplayHeight(dpy, screen)) / DisplayHeightMM(dpy, screen); + + fb_width_mm = (25.4 * fb_width) / dpi; + fb_height_mm = (25.4 * fb_height) / dpi; + } + else + { + fb_width_mm = DisplayWidthMM(dpy, screen); + fb_height_mm = DisplayHeightMM(dpy, screen); + } + } + + set_panning(); + set_gamma(); + apply(); + + XSync(dpy, False); + XCloseDisplay(dpy); + + set_default_global(); + + return 0; +} + +int XInfo(x_info *info) +{ + setenv("DISPLAY", ":0.0", 0); + + char *display_name = NULL; + int event_base, error_base; + int major, minor; + + dpy = XOpenDisplay(display_name); + + if (dpy == NULL) + { + fprintf(stderr, "Can't open display %s\n", XDisplayName(display_name)); + exit(1); + } + if (screen < 0) + screen = DefaultScreen(dpy); + if (screen >= ScreenCount(dpy)) + { + fprintf(stderr, "Invalid screen number %d (display has %d)\n", screen, ScreenCount(dpy)); + exit(1); + } + + root = RootWindow(dpy, screen); + + if (!XRRQueryExtension(dpy, &event_base, &error_base) || !XRRQueryVersion(dpy, &major, &minor)) + { + fprintf(stderr, "RandR extension missing\n"); + exit(1); + } + if (major > 1 || (major == 1 && minor >= 2)) + has_1_2 = True; + if (major > 1 || (major == 1 && minor >= 3)) + has_1_3 = True; + if (major > 1 || (major == 1 && minor >= 4)) + has_1_4 = True; + if (major > 1 || (major == 1 && minor >= 5)) + has_1_5 = True; + + if (!has_1_5) + { + printf("RandR 1.5 not supported\n"); + exit(0); + } + + get_screen(False); + get_monitors(False); + get_crtcs(); + get_outputs(); + + if (monitors) + { + info->count = monitors->n; + + for (int m = 0; m < monitors->n; m++) + { + strcpy(info->monitor[m].name, XGetAtomName(dpy, monitors->monitors[m].name)); + info->monitor[m].primary = monitors->monitors[m].primary ? 1 : 0; + info->monitor[m].width = monitors->monitors[m].width; + info->monitor[m].height = monitors->monitors[m].height; + } + + XCloseDisplay(dpy); + + free(monitors); + set_default_global(); + + return 0; + } + + XCloseDisplay(dpy); + set_default_global(); + + return 1; +} diff --git a/xrandr.h b/xrandr.h new file mode 100644 index 0000000..c6bc869 --- /dev/null +++ b/xrandr.h @@ -0,0 +1,30 @@ +/* + * xrandr.h + * + * Created on: 11 июл. 2022 г. + * Author: alexander + */ + +#ifndef XRANDR_H_ +#define XRANDR_H_ + +typedef struct +{ + char name[10]; + char *ptrName; + char *ptrIndex; + int width; + int height; + int primary; +} x_monitor; + +typedef struct +{ + int count; + x_monitor monitor[5]; +} x_info; + +int XInfo(x_info *info); +x_info *getXInfo(); + +#endif /* XRANDR_H_ */ diff --git a/xrandr_broker.c b/xrandr_broker.c new file mode 100644 index 0000000..1dcb81f --- /dev/null +++ b/xrandr_broker.c @@ -0,0 +1,29 @@ +/* + * xrandr_broker.c + * + * Created on: 11 июл. 2022 г. + * Author: alexander + */ + +#include "xrandr.h" + +#include +#include +#include + +x_info *getXInfo() +{ + x_info *monitors = (x_info *) malloc(sizeof(x_info)); + XInfo(monitors); + + for (int i = 0; i < monitors->count; ++i) + { + monitors->monitor[i].ptrName = (char *)malloc(sizeof(char) * strlen(monitors->monitor[i].name)); + monitors->monitor[i].ptrIndex = (char *)malloc(sizeof(char) * 3); + + strcpy(monitors->monitor[i].ptrName, monitors->monitor[i].name); + sprintf(monitors->monitor[i].ptrIndex, "%d", i + 1); + } + + return monitors; +}