LINUX.ORG.RU

Segfault при установке иконки окна

 ,


0

1

На помощь призывается Zubok

Уже совсем отчаялся и делаю так:

void sml_window_icon(sml_window * win, sml_raster * icon) {
    unsigned long * data = NULL;
    u32   count = 0;
    u32   x;
    u32   y;

    if (!win) {
        sml_setxerrno(BadWindow);
        return;
    }

    if (!icon){
        sml_setxerrno(BadWindow);
        return;
    }

    if (icon->size.x != icon->size.y) {
        sml_setxerrno(BadPixmap);
        return;
    }

    data = bxi_malloc((16 * 16 + 2 + 32 * 32 + 2) * sizeof(unsigned long));
    data[count++] = 16;
    data[count++] = 16;
    for (y = 0; y < 16; y++)
    for (x = 0; x < 16; x++)
        data[count++] = 0xffffffffu;
    data[count++] = 32;
    data[count++] = 32;
    for (y = 0; y < 32; y++)
    for (x = 0; x < 32; x++)
        data[count++] = 0xffffffffu;

    sml_x(XChangeProperty(sml_xdisplay(), win->widget->xwindow,
                          sml_atom_wmicon, sml_atom_cardinal,
                          32, PropModeReplace,
                          (const u8 *)&data, count));
}
Сегфолтится в XChangeProperty. Изначально пытался послать реальный размер и реальную картинку 128х128. Падало. От картинки 16х16 НЕ падает и valgrind говорит что все хорошо. 32х32 - падает и valgrind говорит что invalid read 8 в функции _XData32.

Обкурился манами, везде у всех в 2004м так же падало, но у них было uint32 вместо unsigned long, под х64 он 8 байт. https://github.com/SFML/SFML/pull/1171/commits/7fe96d1ba385269c5d7de8de624b12...

На 32х битной системе все работало (непереносимо на х64) вот так:

    uint32_t * data = malloc(memsize * sizeof(uint32_t));

    if (!(data))
        return SML_ERR_BADALLOC;

    int32_t count = 0;

    {
        data[count++] = 16;
        data[count++] = 16;
        memcpy(data + count, warehouse.elem[image16].data.img.image->data, 16 * 16 * 4);
        count += 16 * 16;
    }

    {
        data[count++] = 32;
        data[count++] = 32;
        memcpy(data + count, warehouse.elem[image32].data.img.image->data, 32 * 32 * 4);
        count += 32 * 32;
    }

    SML_CHECKXFNC(XChangeProperty(warehouse.screen.display,
                                  warehouse.elem[index].data.win.window,
                                  warehouse.atoms.wmicon,
                                  warehouse.atoms.cardinal,
                                  32,  PropModeReplace,
                                  (const unsigned char *) data, count));

★★★★★

Тысяча извинений. Никогда не сталкивался с твоей проблемой, но: ты на чистом арабском пишешь, что кардинал у тебя 32-битный (угу, в документации так же), но манипулируешь с 64-разрядными записями.

Как ты собирался туда записывать октеты?

anonymous
()

С виду все нормально. Надо подумать. Пока сходу не сообразил, в чем проблема.

везде у всех в 2004м так же падало, но у них было uint32 вместо unsigned long, под х64 он 8 байт.

На 32х битной системе все работало (непереносимо на х64) вот так:

Да, этот код точно некорректный и непереносимый. В документации на функцию ясно написано, что для format=32 тип должен быть всегда long вне зависимости от разрядности платформы, а xlib уже разберется. Использование uint32_t — ошибка.

u32 count = 0;, кстати, тоже лучше определить как int, а не настаивать на u32, раз ты его передаешь в функцию. Мелочь, но это в ту же степь, что и с long.

sml_atom_wmicon, sml_atom_cardinal

А это у тебя все правильно определено? Я просто не знаю, как там у тебя.

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

Создал MVE:

#include <stdlib.h>
#include <X11/Xlib.h>

Window    window;
Display * display;
int       screen;

static void _window_create(void) {
    display = XOpenDisplay(getenv("DISPLAY"));
    screen  = DefaultScreen(display);

    window = XCreateWindow(display, RootWindow(display, screen),
                           0, 0, 300, 400, 0,
                           DefaultDepth(display, screen), InputOutput,
                           DefaultVisual(display, screen), 0, NULL);
}

static void _window_propertize(void) {
    unsigned long * data = malloc((16 * 16 + 2 + 32 * 32 + 2) * sizeof(unsigned long));
    int x, y;
    unsigned count = 0;

    data[count++] = 16;
    data[count++] = 16;
    for (y = 0; y < 16; y++)
    for (x = 0; x < 16; x++)
        data[count++] = 0xffffffffu;
    data[count++] = 32;
    data[count++] = 32;
    for (y = 0; y < 32; y++)
    for (x = 0; x < 32; x++)
        data[count++] = 0xffffffffu;

    XChangeProperty(display, window,
                    XInternAtom(display, "_NET_WM_ICON", False), 
                    XInternAtom(display, "CARDINAL", False),
                    32, PropModeReplace,
                    (const unsigned char *)&data, count);
    
}

int main(void) {
    _window_create();
    _window_propertize();

    return 0;
}
gcc main.c -lX11
./a.out
Ошибка сегментирования

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

32, PropModeReplace,

Это особенность X-сервера. На 32х битных машинах 32 бита, на 64х битных - 64.

Но ты-то пишешь 32 (как в документации). Как клиент может вообще знать разрядность сервера (и наоборот)? Что-то там про трусы и крестик. Ради любопытства попробуй и так и так. И да, еще раз: как ты в 64 бита собирался писать ARGB?

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

Это особенность X11:
https://github.com/mirror/libX11/blob/b676e62377483df77bcb6472d26b24f901323fa...

    switch (req->format) {
      case 8:
	<..>
        break;

      case 16:
	<..>
	break;

      case 32:
	len = nelements;
	if (dpy->bigreq_size || req->length + len <= (unsigned) 65535) {
	    SetReqLen(req, len, len);
	    len = (long)nelements << 2;
	    Data32 (dpy, (_Xconst long *) data, len);
	} /* else force BadLength */
	break;

      default:
        /* BadValue will be generated */ ;
      }
long 64 бита на 64 битных системах.

В исходниках GTK и прочих тулкитов написано то же самое что у меня в MVE выше. Однако у меня оно крашится.

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

А собери с санитайзером?

$ g++ main.c -lX11 -fpermissive -fsanitize=address
$ ./a.out 

Только free(data); добавь, а то это прямо очевидная ошибка.

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

Я просто мега кретин.

Неправда. Ты — молодец. Это и есть настоящая работа. Тем не менее, я накатал себе тестовый примерчик: с unsigned *data работает, с unsigned long *data — нет.

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

У меня работает с unsigned long. Иконка устанавливается какая и подразумевалась. Какая у вас разрядность?

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

У меня работает с unsigned long. Иконка устанавливается какая и подразумевалась. Какая у вас разрядность?

64, ясен-красен. 32 сейчас редкость. Вот мой тест:

#include <stdlib.h>
#include <string.h>
#include <xcb/xproto.h>
#include <iconv.h>

#define HELLO "ɐwʎ ɔ vǝmоɔ dиw"

static void _window_propertize(xcb_connection_t *conn, xcb_drawable_t win)
{
	unsigned *data = malloc((16 * 16 + 2 + 32 * 32 + 2) * sizeof(unsigned));
	int x, y;
	unsigned count = 0;

	data[count++] = 16;
	data[count++] = 16;
	for (y = 0; y < 16; y++)
		for (x = 0; x < 16; x++)
			data[count++] = 0xffffffffu;
	data[count++] = 32;
	data[count++] = 32;
	for (y = 0; y < 32; y++)
		for (x = 0; x < 32; x++)
			data[count++] = 0xffffffffu;

	char szWmIcon[] = "_NET_WM_ICON";
	xcb_intern_atom_cookie_t icook = xcb_intern_atom(conn, 0, strlen(szWmIcon), szWmIcon);
	xcb_intern_atom_reply_t *icon = xcb_intern_atom_reply(conn, icook, NULL);
	xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, icon->atom,
		XCB_ATOM_CARDINAL, 32, count, data);

	free(icon);
	free(data);
}

int main(int argc, char* argv[])
{

	xcb_connection_t *connec = xcb_connect(NULL, NULL);
	xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connec)).data;

	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});
	_window_propertize(connec, window);
	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
()
Ответ на: комментарий от anonymous

Тем не менее, я накатал себе тестовый примерчик: с unsigned *data работает, с unsigned long *data — нет.

Это потому что xcb_change_property требует data в другом формате. В Xlib была попытка отвязаться от представления данных в запросе и сделать как бы более общий сишный интерфейс. То есть 32-битные данные в long, 16-битные - в short, а 8-битные char, а функция уже сама там преобразует к нужному виду, пакует в посылку. В XCB этого всего нет - данные надо передавать как в запросе:

ChangeProperty

window: WINDOW
property, type: ATOM
format: {8, 16, 32}
mode: { Replace, Prepend, Append}
data: LISTofINT8 or LISTofINT16 or LISTofINT32
Errors: Alloc, Atom, Match, Value, Window

Твой unsigned как раз LISTofINT32 и получается, поэтому работает, а unsigned long не работает. А вот в XChangeProperty все будет с точностью до наоборот. Поэтому и получалось, что код, в котором по наивности на 32-битных платформах указали массив uint32_t (и работало), на 64-битных не работал уже. А в XCB лучше настаивать на 32 битах в данном случае на всех платформах.

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

Это потому, что xcb_change_property требует data в другом формате...

Во-первых, спасибо за объяснение.

В Xlib была попытка отвязаться от представления данных в запросе и сделать как бы более общий сишный интерфейс. То есть 32-битные данные в long, 16-битные - в short, а 8-битные char, а функция уже сама там преобразует к нужному виду, пакует в посылку.

«О ужас, ужас, о великий ужас!» Но

В исходниках GTK и прочих тулкитов написано то же самое что у меня в MVE выше.

Меня пугает еще больше.

Моей благодарности не будет предела, если ты скинешь ссылку на man внутренних функций Xorg.

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

Он уже встроен в shell (man XChangeProperty например)

Не. Меня интересует не XChangeProperty (не xcb_change_property), а ChangeProperty.

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

Не. Меня интересует не XChangeProperty (не xcb_change_property), а ChangeProperty.

А-а-а, а то я удивился, будто бы при таком уровне кодирования встал вопрос поиска документации на функции. :)

Это документация непосредствено протокола X11. В данном случае Core Protocol, вокруг которого XCB и построена. Вот тут

https://www.x.org/releases/X11R7.7/doc/index.html

Внизу Protocol specifications: X11 Protocol & Extensions и там X Window System Protocol, Version 11, Release 6.8 [PDF] [TXT]:

https://www.x.org/releases/X11R7.7/doc/xproto/x11protocol.txt

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

Спасибо тебе, добрый человек.

P.S. Еще раз выражу свое негодование на xlib. Ну как же так: кардинал у нас 32 бита, а данные должны передавать 64-разрядные! Попался бы этот негодник моему старшине!

P.P.S. Разумеется, это совершеннейшее нахальство с моей стороны, но нигде не попадалось что-нибудь типа «Writing extension for Xorg»?

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

P.S. Еще раз выражу свое негодование на xlib. Ну как же так: кардинал у нас 32 бита, а данные должны передавать 64-разрядные! Попался бы этот негодник моему старшине!

Не 64-разрядные, а long! И это как бы правильно. В то время, когда создавалась и стандартизировалась xlib (это 80-е), в языке были только те целые типы, которые были. Поэтому для xlib API выбирались массивы тех типов, которые гарантировано вмещали 8-, 16- и 32-битные данные, но не имели гарантированной длины. Нужно же было обеспечить переносимость программ (исходного кода) на другие платформы: DEC, Sun и др., где свои компиляторы и процессоры. А на других платформах все могло быть по-другому. Поэтому решили, что голова по укладке данных в запрос X11 пусть болит у библиотеки xlib на целевой машине. Поэтому в xlib и есть LONG64, которая и определяет, что делать с long в том или ином случае.

#if defined (_LP64) || \
    defined(__alpha) || defined(__alpha__) || \
    defined(__ia64__) || defined(ia64) || \
    defined(__sparc64__) || \
    defined(__s390x__) || \
    (defined(__hppa__) && defined(__LP64__)) || \
    defined(__amd64__) || defined(amd64) || \
    defined(__powerpc64__) || \
    (defined(sgi) && (_MIPS_SZLONG == 64))
#define LONG64
#endif

А какая альтернатива тогда была? Заставить программиста переносимым методом укладывать массив данных побайтно, забив ему голову еще и endianness?

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