LINUX.ORG.RU

Понятия не имею, но я бы для начала заглянул бы в исходники ximagesrc плагина используемого в gstreamer для захвата экрана.

Aber ★★★★★
()
Ответ на: комментарий от Aber

Ну с заглядыванием в сорцы я пока решил повременить создав тут топик, вдруг кто быстро пояснит что это как-то очень легко делается, или наоборот принципиально невозможно.

pon4ik ★★★★★
() автор топика

Есть два пути: xshm (захват всего экрана), и xcomposite (захват отдельного окна). Первый довольно тормозной, но на современном железе это не имеет значение.

Khnazile ★★★★★
()
Ответ на: комментарий от Khnazile

Я правильно понимаю, что он(xshm) берёт битмап который рисуется в видимую область корневого окна?

Есть какой то способ исключить заданное окно из этого битмапа? Что бы оно рисовалось на экране, но при этом не попадало в суммарную картинку корневого окна? Звучит конечно корявенько…

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Такого способа нет. В теории, таким вещами должен заниматься window manager, но я не знаю ни одного под линукс, который такое может.

Khnazile ★★★★★
()
Ответ на: комментарий от pon4ik

Только это не поможет блин…

Ведь его же тоже будет видно в корневом окне. Печалько.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Есть какой то способ исключить заданное окно из этого битмапа? Что бы оно рисовалось на экране, но при этом не попадало в суммарную картинку корневого окна?

Да, можно. Но для этого на XServer нужно патчи наложить.

anonymous
()
Ответ на: комментарий от anonymous

Первый:

--- Xext/security.c.orig	2016-07-19
+++ Xext/security.c	2019-08-12
@@ -41,6 +41,10 @@
 #include "extinit.h"
 #include "protocol-versions.h"
 
+#define IsWManager(pCl) (((pCl)->clientPtr != NULL) && \
+	(EventMaskForClient(GetCurrentRootWindow((pCl)->clientPtr), (pCl)) \
+	& (SubstructureRedirectMask | ResizeRedirectMask)))
+
 /* Extension stuff */
 static int SecurityErrorBase;   /* first Security error number */
 static int SecurityEventBase;   /* first Security event number */
@@ -61,6 +65,7 @@
     unsigned int live       :1;
     unsigned int trustLevel :2;
     XID authId;
+	XID group;
 } SecurityStateRec;
 
 /* Extensions that untrusted clients shouldn't have access to */
@@ -117,9 +122,14 @@
 SecurityDoCheck(SecurityStateRec * subj, SecurityStateRec * obj,
                 Mask requested, Mask allowed)
 {
+	unsigned int subjTrust = subj->trustLevel;
+
+	if (obj->group != None && subj->group != obj->group)
+		subjTrust = XSecurityClientUntrusted;
+
     if (!subj->haveState || !obj->haveState)
         return Success;
-    if (subj->trustLevel == XSecurityClientTrusted)
+    if (subjTrust == XSecurityClientTrusted)
         return Success;
     if (obj->trustLevel != XSecurityClientTrusted)
         return Success;
@@ -142,6 +152,7 @@
     state->trustLevel = XSecurityClientTrusted;
     state->haveState = TRUE;
     state->live = FALSE;
+    state->group = None;
 }
 
 /*
@@ -770,6 +781,8 @@
             allowed |= DixReadAccess;
     }
 
+	if (IsWManager(rec->client)) allowed = requested;
+
     if (clients[cid] != NULL) {
         obj = dixLookupPrivate(&clients[cid]->devPrivates, stateKey);
         if (SecurityDoCheck(subj, obj, requested, allowed) == Success)
@@ -836,6 +849,8 @@
     subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
     obj = dixLookupPrivate(&rec->target->devPrivates, stateKey);
 
+	if (IsWManager(rec->client)) allowed = requested;
+
     if (SecurityDoCheck(subj, obj, requested, allowed) != Success) {
         SecurityAudit("Security: denied client %d access to client %d on "
                       "request %s\n", rec->client->index, rec->target->index,
@@ -856,6 +871,8 @@
     subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
     obj = dixLookupPrivate(&wClient(rec->pWin)->devPrivates, stateKey);
 
+	if (IsWManager(rec->client)) allowed = requested;
+
     if (SecurityDoCheck(subj, obj, requested, allowed) != Success) {
         SecurityAudit("Security: denied client %d access to property %s "
                       "(atom 0x%x) window 0x%lx of client %d on request %s\n",
@@ -878,6 +895,8 @@
         subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
         obj = dixLookupPrivate(&wClient(rec->pWin)->devPrivates, stateKey);
 
+		if (IsWManager(rec->client)) return;
+
         if (SecurityDoCheck(subj, obj, DixSendAccess, 0) == Success)
             return;
 
@@ -907,6 +926,8 @@
     subj = dixLookupPrivate(&rec->client->devPrivates, stateKey);
     obj = dixLookupPrivate(&wClient(rec->pWin)->devPrivates, stateKey);
 
+	if (IsWManager(rec->client)) return;
+
     if (SecurityDoCheck(subj, obj, DixReceiveAccess, 0) == Success)
         return;
 
@@ -970,6 +991,7 @@
                 TimerCancel(pAuth->timer);
 
             state->trustLevel = pAuth->trustLevel;
+			state->group = pAuth->group;
         }
         break;

anonymous
()
Ответ на: комментарий от anonymous

Второй.

--- Xext/xace.c.orig	2016-07-19
+++ Xext/xace.c	2019-08-12
@@ -24,6 +24,7 @@
 #include <stdarg.h>
 #include "scrnintstr.h"
 #include "extnsionst.h"
+#include "windowstr.h"
 #include "pixmapstr.h"
 #include "regionstr.h"
 #include "gcstruct.h"
@@ -264,6 +265,40 @@
 
     /* censorRegion = imageRegion - visibleRegion */
     RegionSubtract(&censorRegion, &imageRegion, pVisibleRegion);
+
+	/* process secured windows */
+	if (pDraw->type == DRAWABLE_WINDOW && ((WindowPtr)pDraw)->parent == NULL)
+	{
+		WindowPtr pTemp, pWin;
+		BoxPtr pBoxVR = RegionExtents(pVisibleRegion);
+		pTemp = pDraw->pScreen->root;
+		while (1)
+		{
+			if ((pWin = pTemp->firstChild) == NULL &&
+				(pWin = pTemp->nextSib) == NULL)
+				while (pTemp->parent != NULL &&
+						(pWin = pTemp->parent->nextSib) == NULL)
+					pTemp = pTemp->parent;
+			if (pWin == NULL)
+				break;
+			pTemp = pWin;
+
+			if ((pWin->visibility == VisibilityUnobscured ||
+				pWin->visibility == VisibilityPartiallyObscured) &&
+				(XaceHook(XACE_RESOURCE_ACCESS, client, pWin->drawable.id, RT_WINDOW,
+					pWin, RT_NONE, NULL, DixUseAccess) != Success))
+			{
+				RegionPtr pRegWin = RegionCreate(NullBox, 1);
+				RegionCopy(pRegWin, &pWin->clipList);
+				/* need more investigation... xex */
+				if (pBoxVR->x1 < x)
+					RegionTranslate(pRegWin, pBoxVR->x1 - x, 0);
+				RegionUnion(&censorRegion, &censorRegion, pRegWin);
+				RegionDestroy(pRegWin);
+			}
+		}
+	}
+
     nRects = RegionNumRects(&censorRegion);
     if (nRects > 0) {           /* we have something to censor */
         GCPtr pScratchGC = NULL;

anonymous
()
Ответ на: комментарий от anonymous

Третий.

--- dix/dispatch.c.orig	2016-07-19
+++ dix/dispatch.c	2019-08-12
@@ -1668,6 +1668,67 @@
     else
         pSrc = pDst;
 
+	if (pSrc->type == DRAWABLE_WINDOW && ((WindowPtr)pSrc)->parent == NULL)
+	{
+		long widthBytesLine, linesPerBuf, nlines, height, width, srcX, srcY,
+			dstX, dstY, length;
+		char *pBuf;
+		PixmapPtr pPix;
+		RegionPtr pRgnCopy, pVisibleRegion;
+		height = stuff->height, width = stuff->width, srcX = stuff->srcX,
+			srcY= stuff->srcY, dstX = stuff->dstX, dstY = stuff->dstY;
+		pVisibleRegion = NotClippedByChildren((WindowPtr)pSrc);
+		if (pVisibleRegion)
+			RegionTranslate(pVisibleRegion, -pSrc->x, -pSrc->y);
+
+		widthBytesLine = PixmapBytePad(width, pSrc->depth);
+		if (widthBytesLine == 0 || height == 0)
+			linesPerBuf = 0;
+		else if (widthBytesLine >= IMAGE_BUFSIZE)
+			linesPerBuf = 1;
+		else
+		{
+			linesPerBuf = IMAGE_BUFSIZE / widthBytesLine;
+			if (linesPerBuf > height)
+				linesPerBuf = height;
+		}
+		length = linesPerBuf * widthBytesLine;
+		if (linesPerBuf < height)
+		{
+			while ((linesPerBuf > 1) &&
+				(length & ((1L << LOG2_BYTES_PER_SCANLINE_PAD) - 1)))
+			{
+				linesPerBuf--;
+				length -= widthBytesLine;
+			}
+			while (length & ((1L << LOG2_BYTES_PER_SCANLINE_PAD) - 1))
+			{
+				linesPerBuf++;
+				length += widthBytesLine;
+			}
+		}
+		pBuf = calloc(1, length);
+
+		for (long linesDone = 0; height - linesDone > 0; linesDone += nlines)
+		{
+			nlines = min(linesPerBuf, height - linesDone);
+			(pSrc->pScreen->GetImage) (pSrc, srcX, srcY + linesDone, width,
+				nlines, ZPixmap, ((unsigned long)~0L), (void*)pBuf);
+			if (pVisibleRegion)
+				XaceCensorImage(client, pVisibleRegion, widthBytesLine, pSrc,
+					srcX, srcY + linesDone, width, nlines, ZPixmap, pBuf);
+			pPix = GetScratchPixmapHeader(pSrc->pScreen, width, nlines,
+				pSrc->depth, pSrc->bitsPerPixel, widthBytesLine, (void *)pBuf);
+			pRgnCopy = (*pGC->ops->CopyArea) (&pPix->drawable, pDst, pGC, 0, 0,
+				width, nlines, dstX, dstY + linesDone);
+			if(pRgnCopy)
+				RegionDestroy(pRgnCopy);
+			FreeScratchPixmapHeader(pPix);
+		}
+
+		free(pBuf);
+	}
+	else
     pRgn = (*pGC->ops->CopyArea) (pSrc, pDst, pGC, stuff->srcX, stuff->srcY,
                                   stuff->width, stuff->height,
                                   stuff->dstX, stuff->dstY);

anonymous
()
Ответ на: комментарий от anonymous

Ну и код, чтобы оттестировать.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/extensions/security.h>
#include <xcb/xcb.h>
#include <iconv.h>

#define PROTO "MIT-MAGIC-COOKIE-1"
#define HELLO "Цой жив!"

int main(int argc, char* argv[])
{
	Display *dpy = XOpenDisplay(NULL);

	int maj, mi;
	if (!XSecurityQueryExtension(dpy, &maj, &mi))
	{
		fprintf(stderr, "Couldn't query Security extension\n");
		return 2;
	}

	Xauth *xin = XSecurityAllocXauth();
	xin->name = PROTO;
	xin->name_length = strlen(PROTO);
	xin->data = HELLO;
	xin->data_length = strlen(HELLO);
	
	XSecurityAuthorizationAttributes xsat = {600, XSecurityClientTrusted, 14};
	XSecurityAuthorization ret;
	Xauth *xout = XSecurityGenerateAuthorization(dpy, xin,
		XSecurityTimeout | XSecurityTrustLevel | XSecurityGroup, &xsat, &ret);

	xcb_auth_info_t auth = {.name = xout->name, .namelen = xout->name_length,
		.data = xout->data, .datalen = xout->data_length};

	xcb_connection_t *connec = xcb_connect_to_display_with_auth_info(NULL, &auth, NULL);
	xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connec)).data;
	XSecurityFreeXauth(xin);
	XSecurityFreeXauth(xout);
	XCloseDisplay(dpy);

	xcb_drawable_t window = xcb_generate_id(connec);
	xcb_create_window(connec, XCB_COPY_FROM_PARENT, window, screen->root,
		0, 0, 200, 100, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
		screen->root_visual, XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
		(uint32_t[]){screen->white_pixel, XCB_EVENT_MASK_EXPOSURE});
	xcb_map_window(connec, window);

	char font_name[] = "-*-fixed-medium-r-normal--18-*-iso10646-1";
	xcb_font_t font = xcb_generate_id(connec);
	xcb_open_font(connec, font, strlen(font_name), font_name);

	xcb_gcontext_t gc = xcb_generate_id(connec);
	xcb_create_gc(connec, gc, window,
		XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT,
		(uint32_t[]){screen->black_pixel, screen->white_pixel, font});

	iconv_t cd = iconv_open("UCS-2BE", "UTF-8");
	size_t sz = strlen(HELLO), szout = sz * sizeof(xcb_char2b_t), szlen = szout;
	char *temp = HELLO;
	xcb_char2b_t *buffer = calloc(sz + 1, sizeof(xcb_char2b_t)), *bout = buffer;
	iconv(cd, &temp, &sz, (char**)&bout, &szout);
	iconv_close(cd);
	szlen -= szout, szlen /= sizeof(xcb_char2b_t);

	xcb_query_text_extents_cookie_t ecook = xcb_query_text_extents(connec,
		gc, szlen, buffer);
	xcb_query_text_extents_reply_t *ext = xcb_query_text_extents_reply(connec,
		ecook, NULL);

	xcb_flush(connec);

	xcb_generic_event_t *event = 0;
	xcb_get_geometry_cookie_t gcook;
	xcb_get_geometry_reply_t *geom;
	while (free(event), event = xcb_wait_for_event(connec))
	{
		switch (event->response_type & ~0x80)
		{
			case XCB_EXPOSE:
			gcook = xcb_get_geometry(connec, window);
			geom  = xcb_get_geometry_reply(connec, gcook, NULL);
			xcb_image_text_16(connec, szlen, window, gc,
				(geom->width - ext->overall_width) / 2,
				(geom->height - ext->font_descent + ext->font_ascent) / 2, buffer);
			free(geom);
			xcb_flush(connec);
			break;
		}
	}
	return 0;
}

anonymous
()

Интересуюсь с целью получения возможности прятать окно во время показа.

Окошки можно прятать (куда прятать и как возвращать?) при помощи оконных менеджеров или сторонних инструментов. Я вообще с самого начала и из последующих объяснений не понял, при чем тут захват экрана и как он связан с показыванием окошка.

А так: окошки прятать и показывать можно, захватывать экран можно.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Чувак хочет стримить содержимое экрана, чтобы при этом одно из окон было видно ему но не смотрящим стрим. Что непонятно-то?

hateyoufeel ★★★★★
()

Вынос окна, не предназначенного для показа на другой дисплей (реальный или виртуальный) решает все проблемы.

Elyas ★★★★★
()
Ответ на: комментарий от hateyoufeel

Вот было непонятно, но вот сейчас уже понятно. Из первого сообщения я вообще не понял, о каком поведении речь.

Zubok ★★★★★
()

Ну, один способ тебе уже сказали: убрать окно на другой экран. Я могу еще предложить из простых вариантов без второго дисплея: - отодвинуть окно куда-нибудь вбок, а скринкастить только выбранную область экрана, в которую твой чат не попадает.

Zubok ★★★★★
()

Еще есть вариант, но я пока сразу не знаю, что и как получится, поэтому он умозрительный. Запустить вложенный X-Server (Xephyr), в нем запустить необходимый сетап для скринкаста и скринкастить только его содержимое (а это просто окно), а на основном сервере запустить свой чат. Он в стрим не попадет.

С Xephyr есть проблема только - там (пока) нет аппаратного ускорения и будет программный 3D-рендеринг.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

Мысль интересная, но это секс с x2go/xpra or die. Хотя, для некоторых демонстраций - годный вариант.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Ну тогда еще вариант: запустить просто второй X-Server на другом VT и оттуда скринкастить. Но тогда чтобы глядеть чат, надо будет VT переключать на другой сервер. В случае с Xephyr чат может быть перед глазами где-то и можно просто его поднимать и посматривать. Никто этого не увидит в эфире.

Zubok ★★★★★
()
Ответ на: комментарий от Elyas

Ты про x11 дисплей? И вот чё, мне каждое окошко в отдельном зефире запускать? Нее, это не наш метод, тем более интеграция через d-bus и прочее будет так себе работать скорее всего.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от anonymous

К сожалению, мне даже тестовый код нужно осиливать, т.е. патчи эти я если и применю, то не скоро. Будем почитать, спасибо, но времени это потребует явно больше чем я рассчитывал изначально.

pon4ik ★★★★★
() автор топика

Нужно транслировать именно через браузер? Если катит обычная трансляция на одной из популярных стриминговых платформ, то можно взять OBS и настроить сцену так, чтобы показывалось только нужное

Midael ★★★★★
()
Ответ на: комментарий от Midael

Именно через браузер. В идеале ещё что бы и запись так же работала.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от Zubok

Сдаётся мне, вариант с Xephyr не взлетит, даже проверять лень. Ведь окно которое рисует эмуляцию X11 всё равно останется частью корневого окна. Ой походу придётся патчи анона читать :(

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Можешь на человечьем кратенько суть изменений и чего почитать по теме кроме мана по api x11?

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Так нет же! Ты скринкаст делай именно Xephyr. Укажи его софту, который скринкастит, и все. А чат СНАРУЖИ! Я же и написал, что сетап, который скринкастишь именно в Xephyr пускай.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от pon4ik

Я просто выдумываю варианты, чтобы можно было как-то сделать существующими средствами, не прибегая к программированию и написанию какого-то хитрого софта. А есть ли вообще такая возможность, надо подумать.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Так нет же! Ты скринкаст делай именно Xephyr. Укажи его софту, который скринкастит, и все.

Или софт для скринкаста запусти внутри Xephyr и скажи стримить весь десктоп. Могу и сам попробовать, но что-то как-то... лень. :)

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

А, да, точно чет я затупил.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Не, я имел в виду скорее screen - если в терминах X11.

Elyas ★★★★★
()
Ответ на: комментарий от pon4ik

Можешь на человечьем кратенько суть изменений и чего почитать по теме кроме мана по api x11?

Тут дело простое, я бы сказал совсем нехитрое. В X'ах есть фреймфорк безопасности XACE, поверх него написаны два раcширения: Security и XSelinux.

Для клиента Security имеется 4 параметра: authId, время жизни, trusted (доверенный или нет) и такое legacy как group, осколок от расширения Application Group. Сейчас сходу и не найдешь, что оно делало. Короче, в Security добавляется то, что клиенты из разных групп — недоверенные друг другу. Если приглядеться, большая часть патча состоит в том, что window manager добавляется в исключения — ему доступ разрешен, да.

Второй патч. В X'ах есть функция с незатейливым именем — XaceCensorImage, ага. Пробегаемся по всему дереву дереву окон, проверяем доступ клиента к ним, окно видимое и доступа к нему у клиента нет — исключаем его из картинки.

Третий патч, просто добавляется цензурирование картинки для для CopyArea (не лучшим способом, можно улучшить); для GetImage вызов XaceCensorImage уже есть.

В тестовом примере клиент генерирует себе аутентификацию на 600 секунд с группой под номером 14. Ни ввод послушать, ни снять изображение с него не выйдет — в полноэкранном приложении на его месте будет черный прямоугольник. Для произвольного приложения: перед запуском «особенного» клиента с помощью xauth (прочтешь его man) генерирутся доверенная аутентификация с ненулевым group-id. После запуска приложения не забываем возвращать положение в исходное!

anonymous
()
Ответ на: комментарий от anonymous

в полноэкранном приложении на его месте будет черный прямоугольник.

Excuse-moi, эту фразу следует читать как, при снятии скриншота на месте этого окна будет черный прямоугольник.

anonymous
()
Ответ на: комментарий от anonymous

Идею понял, с тем что из патча сходу сообразил - матчится, спасибо за пояснения. Возникает резонный вопрос - если есть фрейморк и понятие «расширение», возможно есть какая то дырка что бы можно было сделать плагин к ванильному решению?

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Возникает резонный вопрос - если есть фрейморк и понятие «расширение», возможно есть какая то дырка что бы можно было сделать плагин к ванильному решению?

Плагин к ванильному решению в X'ах — это и есть расширение. Тебе нужно определиться том, что ты хочешь конкретно. Тут ползователь тусовался, kirk_johnson, он высказывал свои мысли и пожелания. Было бы неплохо услышать его мнение.

А насчет прозвучавших конкретных хотелок — эти патчи просто решают высказанные проблемы грубой силой.

И да, в отличии от меня (я — бсдюк), у тебя есть еще XSelinux. Не знаю просто, насколько для тебя важна твоя проблема, сколько ты готов положить на нее сил. Я задался целью, чтобы приложение могло защитить свой ввод-вывод в X'ах, выкатил решение. Если что непонятно в коде (по мне, он тривиален), спрашивай.

anonymous
()
Ответ на: комментарий от anonymous

XSelinux

Почему-то не гуглится.

Сил много не готов положить. За poc - спасибо, но я видимо максимум потрачу ещё какое-то время на поиск менее интрузивного решения.

Тебе нужно определиться том, что ты хочешь конкретно

Конкретно - хочу задавать список приложений/окон которым можно попадать в захват экрана и которым нельзя в простом декларативном конфиге. Имхо это оценила бы и более широкая аудитория, странно, что готового решения до сих пор нет.

pon4ik ★★★★★
() автор топика
Последнее исправление: pon4ik (всего исправлений: 1)
Ответ на: комментарий от pon4ik

XSelinux

Да, ладно. https://www.altlinux.org/Slx http://selinuxproject.org/page/Experimenting_With_X-Windows

Конкретно - хочу задавать список приложений/окон которым можно попадать в захват экрана и которым нельзя в простом декларативном конфиге.

Там типа того и есть. Если разберешься, люди скажут тебе огромное человеческое спасибо. На вид — похоже на пыталище. Патчи на Security выглядят попроще.

Сил много не готов положить.

Тут даже нормальное ТЗ будет выглядеть как немалый труд, но если сделаешь грамотно — опять же тебе будет огромное человеческое спасибо.

anonymous
()
Ответ на: комментарий от anonymous

Вторая стадия - торг :)

Если я запилю требования к интерфейсу, готов ли ты поресёрчить неинтрузивный подход(что бы можно было ванильную сборку расширить сторонним приложением/библиотекой)?

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Почитал статью из altlinux- если это работает в ванильной версии, то звучит годно и мне вполне подойдёт, надо пока потыкать этот подход.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Блин, это только для altlinux…

Нет, для XSelinux.

добро пожаловать в АД

Обнадёживает…

Выглядит даже хуже.

anonymous
()
Ответ на: комментарий от anonymous

А что физически из себя представляет расширение X11? Это спека расширения протокола, либа? Какой интерфейс у расширений?

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

А что физически из себя представляет расширение X11?

костыли на каждый чих, которые некоторые называют крутой расширяемостью исков. Популярные костыли потом описывают в спеках и принимают в майнстрим а непопулярные так и волочатся в виде патчей. В общем как обычный опенсорсный проект развивается :)

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.