LINUX.ORG.RU

X11 CLIPBOARD: Параметры XChangeProperty для ответа на SelectionRequest

 , , ,


1

2

Уважаемые обитатели форума!
Пытаюсь сделать поле текстового ввода для своей небольшой программы. Дошел до copy/paste. На основе ряда примеров, в том числе с вашего сайта, добавил «импорт» текста из CLIPBOARD. Но на этом мой оптимизм закончился: вторую неделю пытаюсь отправить тестовую строку «Hello World!» другим приложениям. Перевернул всю документацию и те немногие доступные примеры, которые есть в интернете. Мне кажется, я что-то фундаментально недопонял/пропустил в работе XChangeProperty, вызываемой перед XSendEvent для отправки SelectionNotify. Событие отправляется, но, например, в Plume я не могу вставить отправленный текст. Ниже привожу код (работающий «импорт» и неработающий «экспорт»).

void MkProgWin(Prog *prog)
{
	//...
	prog->clip.atom.utf8 =
		XInternAtom (prog->dpy, "UTF8_STRING", True);
	prog->clip.atom.clipboard =
		XInternAtom (prog->dpy, "CLIPBOARD", False); //==XA_CLIPBOARD(prog->dpy)
	prog->clip.atom.target =
		XInternAtom (prog->dpy, "TARGETS", False);
	//...
}

//-----------------------------------------------------------------------------
void WidgetClipboardSetOwner (Widget *widget)
{
	if(widget->prog->event.type == ButtonPress)
	if(widget->prog->event.xbutton.button == 3)
	if(widget->prog->event.xbutton.window == widget->win)
	if(widget->prog->cursor.win_id == widget->id)
	if(widget->cursor.symbol != NULL)
	{
		XSetSelectionOwner(
			widget->prog->dpy,
			widget->prog->clip.atom.clipboard,
			widget->win, CurrentTime);

		XFlush(widget->prog->dpy);
	}
}
//-----------------------------------------------------------------------------
void WidgetClipboardSendData (Widget *widget)
{
	if(widget->prog->event.type == SelectionRequest)
	if(widget->prog->event.xselectionrequest.owner == widget->win)
	if(widget->prog->event.xselectionrequest.selection == 
			widget->prog->clip.atom.clipboard)
	{
		XSelectionRequestEvent *req = &(widget->prog->event.xselectionrequest);

		if(req->target == widget->prog->clip.atom.target)
		{
			char text[] = "Hello World";
			size_t sizze = strlen(text); // +1?
			XChangeProperty(
				widget->prog->dpy,
				req->requestor,
				req->property,	/* widget->prog->clip.atom.clipboard */
				req->target,	/* XA_TARGETS(widget->prog->dpy) XA_UTF8_STRING(widget->prog->dpy) XA_STRING */
				8,
				PropModeReplace,
				(unsigned char *) text,
				sizze);

			XEvent respond;
			respond.xselection.display = widget->prog->dpy;
			respond.xselection.type = SelectionNotify;
			respond.xselection.send_event = True;
			respond.xselection.time = req->time;
			respond.xselection.requestor = req->requestor;
			respond.xselection.selection = req->selection;
			respond.xselection.target = req->target, // widget->prog->clip.atom.utf8
			respond.xselection.property = req->property;

			int status = XSendEvent(
				widget->prog->dpy,
				req->requestor,
				False,			//True
				NoEventMask,	// NULL 0L
				&respond);
			XFlush(widget->prog->dpy);

			printf("status == %d\n", status);
		}

		char *an;
		if(XFetchName(
			widget->prog->dpy,
			widget->prog->event.xselectionrequest.requestor,
			&an) != 0) {
			printf("Requestor: %s\n", an);
			XFree(an);
		}

		an = XGetAtomName(
			widget->prog->dpy,
			widget->prog->event.xselectionrequest.selection);
		if(an != NULL) {
			printf("Selection: %s\n", an);
			XFree(an);
		}
		an = XGetAtomName(
			widget->prog->dpy,
			widget->prog->event.xselectionrequest.target);
		if(an != NULL) {
			printf("Target: %s\n", an);
			XFree(an);
		}
		an = XGetAtomName(
			widget->prog->dpy,
			widget->prog->event.xselectionrequest.property);
		if(an != NULL) {
			printf("Property: %s\n", an);
			XFree(an);
		}
		printf("--------------------\n");
	}
}

Вот что печает терминал:

status == 1
Requestor: Pluma
Selection: CLIPBOARD
Target: TARGETS
Property: GDK_SELECTION
--------------------
status == 1
Requestor: Caja
Selection: CLIPBOARD
Target: TARGETS
Property: GDK_SELECTION
--------------------
status == 1
Requestor: Caja
Selection: CLIPBOARD
Target: TARGETS
Property: GDK_SELECTION
--------------------

// Работающий код, но в программе он полностью закомментирован
// поскольку тоже реагирует на Button3Press
void WidgetClipboardGetData (Widget *widget) // Работает
{
    if(widget->prog->event.type == ButtonPress)
    if(widget->prog->event.xbutton.button == 3)
    if(widget->prog->cursor.win_id == widget->id)
    {
        Window win = XGetSelectionOwner( 
                        widget->prog->dpy,
                        widget->prog->clip.atom.clipboard);
        if(win != None)
        {
            XConvertSelection ( widget->prog->dpy,
                                widget->prog->clip.atom.clipboard,
                                widget->prog->clip.atom.utf8,
                                widget->prog->clip.atom.target,
                                widget->win, CurrentTime );

            XFlush(widget->prog->dpy);
            usleep(5000);
            XEvent e;
            
            if(XCheckTypedEvent(widget->prog->dpy, SelectionNotify, &e))
            if(e.xselection.property != None)
            {
                u_char*    data        = NULL;
                Atom    type        = None;
                int        format        = 0;
                u_long    nitems        = 0;
                u_long    bytes_left     = 0;

                if(Success == 
                    XGetWindowProperty( widget->prog->dpy,
                                        e.xselection.requestor,
                                        e.xselection.property,
                                        0L,
                                        0L,
                                        False,
                                        widget->prog->clip.atom.utf8,
                                        &type,
                                        &format,
                                        &nitems,
                                        &bytes_left,
                                        &data))
                {
                    if(bytes_left > 0)
                    {
                        size_t bytes = (size_t)bytes_left;
                        char *text = (char*)malloc(bytes);
                        if(text != NULL)
                        {            
                            if(Success == XGetWindowProperty(
                                            widget->prog->dpy,
                                            e.xselection.requestor,
                                            e.xselection.property,
                                            0L,
                                            bytes_left,
                                            False,
                                            widget->prog->clip.atom.utf8,
                                            &type,
                                            &format,
                                            &nitems,
                                            &bytes_left,
                                            &data))
                            {

                                strncpy(text, (char*)data, bytes);
                                printf("%s\n", text);
                                free(text);
                                XFree(data);
                            }
                        }
                    }
                }            
            }
        }
    }
}

ОГРОМНОЕ СПАСИБО ЗА ПОМОЩЬ!!! ВОТ РЕШЕНИЕ:

void MkProgWin(Prog *prog)
{
	//...
	prog->clip.atom.clipboard =
		XInternAtom (prog->dpy, "CLIPBOARD", False);
	prog->clip.atom.target =
		XInternAtom (prog->dpy, "TARGETS", False);
	prog->clip.atom.utf8 =
		XInternAtom (prog->dpy, "UTF8_STRING", False);
	prog->clip.atom.xa_string =
		XInternAtom (prog->dpy, "XA_STRING", False);
	prog->clip.atom.xa_text =
		XInternAtom (prog->dpy, "XA_TEXT", False);
	//...
}

//-----------------------------------------------------------------------------
void WidgetClipboardSetOwner (Widget *widget)
{
	if(widget->prog->event.type == ButtonPress)
	if(widget->prog->event.xbutton.button == 3)
	if(widget->prog->event.xbutton.window == widget->win)
	if(widget->prog->cursor.win_id == widget->id)
	if(widget->cursor.symbol != NULL)
	{
		XSetSelectionOwner(
			widget->prog->dpy,
			widget->prog->clip.atom.clipboard,
			widget->win, CurrentTime);

		XFlush(widget->prog->dpy);
	}
}
//-----------------------------------------------------------------------------
void WidgetClipboardSendData (Widget *widget)
{
	if(widget->prog->event.type == SelectionRequest)
	if(widget->prog->event.xselectionrequest.owner == widget->win)
	if(widget->prog->event.xselectionrequest.selection == 
			widget->prog->clip.atom.clipboard)
	{
		XSelectionRequestEvent *req = &(widget->prog->event.xselectionrequest);
		XEvent respond;
		respond.xselection.display = widget->prog->dpy;
		respond.xselection.type = SelectionNotify;
		respond.xselection.send_event = True;
		respond.xselection.time = req->time;
		respond.xselection.requestor = req->requestor;
		respond.xselection.selection = req->selection;
		respond.xselection.target = req->target;
		respond.xselection.property = req->property;

		if(req->target == widget->prog->clip.atom.target)
		{
			Atom supported[] = { 
				widget->prog->clip.atom.target,
				widget->prog->clip.atom.utf8,
				widget->prog->clip.atom.xa_string,
				widget->prog->clip.atom.xa_text
				};

			XChangeProperty(
				widget->prog->dpy,
				req->requestor,
				req->property,
				req->target,
				32, 
				PropModeReplace,
				(unsigned char *) (&supported),
				sizeof(supported) / sizeof(supported[0]));
		}
		else
		if((req->target == widget->prog->clip.atom.utf8)
		|| (req->target == widget->prog->clip.atom.xa_string)
		|| (req->target == widget->prog->clip.atom.xa_text)) // то, что требовал GTK
		{
			char text[] = "HELLO WORLD! ___(UTF8)___!!!____";

			XChangeProperty(
				widget->prog->dpy,
				req->requestor,
				req->property,
				req->target,
				8, 
				PropModeReplace,
				(unsigned char *) text,
				strlen(text));
		}
		else reply.xselection.property = None;

		XSendEvent(
				widget->prog->dpy,
				req->requestor,
				False,
				NoEventMask,
				&respond);
			XFlush(widget->prog->dpy);
	}
}


Последнее исправление: dimon1980 (всего исправлений: 8)

Вот зачем тебе голые иксы? Взял бы культи или гтк там.

Ты, похоже, не обрабатываешь TARGETS как положено. Сначала приложения запрашивают у тебя формат TARGETS, чтобы узнать, что ты можешь им дать. Надо им вернуть массив атомов с именами форматов (тем же UTF8_STRING). Потом они уже у тебя запросят интересующий их формат.

ilammy ★★★
()

ilammy абсолютно прав.
Не стоит на запрос TARGETS отвечать строкой:)
Отдайте для теста просто атом TEXT для начала.
Следом прилетит реквест уже именно на него.
Тогда и отдадите данные.
Если в дальнейшем планируете передавать большие объемы почитайте про INCR

Ну или дополнительно смотрим «Энциклопедию Юных Сурков» раздел ICCCM

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

Спасибо. Учту.

В любом случае - надо разобраться досконально. Документация по иксам основательная, но много недосказанностей.

dimon1980
() автор топика
Ответ на: Спасибо. Учту. от dimon1980

РЕШЕНО. СПАСИБО.

ОТВЕТ ПОМЕСТИЛ В КОНЦЕ ВОПРОСА.

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