Открытие сокетов на Xserver напрямую

Я хочу понять, как среда рабочего стола Linux работает с Xserver. Я читал, что большинство оконных менеджеров не открывают сокеты напрямую, вместо этого они используют либо привязки Xlib, для какого языка пишется WM, либо вы можете использовать привязки более высокого уровня XCB; но я хотел бы знать, каковы преимущества открытия сокета непосредственно на Xserver?


person user961559    schedule 24.08.2012    source источник


Ответы (3)


Вопрос скорее должен звучать так: «Каковы преимущества использования Xlib вместо графического набора инструментов, такого как gtk»?

Даже чтобы освоить Xlib, нужно потратить месяцы или годы! Это настолько сложно, что почти каждое приложение, имеющее графическое содержимое для отображения, вместо этого использует набор инструментов.

Использование простого сокета означает прямое обращение к протоколу X11, что означает, что вы в конечном итоге воссоздаете Xlib (нет, вы не закончите!)

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

person Jo So    schedule 24.08.2012

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

Некоторое время назад я нашел пример кого-то этого, и я больше не могу его найти, но вот более или менее его код. Довольно интересно узнать, что происходит под капотом таких клиентских библиотек, как XCB.

Для того, чтобы отправить запрос на X-сервер, вам необходимо:

  1. сокетное соединение с X-сервером
  2. опкод запроса
  3. данные, связанные с запросом

API для открытия сокета, создания графического контекста, создания окна и сопоставления окна может выглядеть примерно так:

x11_connection_t connection = {0};
x11_init_connection(&connection);

x11_state_t state;
state.socket_fd   = connection.socket_fd;
state.id_base     = connection.setup->id_base;
state.root_window = connection.root[0].window_id;
state.root_visual = connection.root[0].visual_id;
state.root_depth  = connection.root[0].depth;

state.gcontext = x11_init_gc(&state, X11_GC_GRAPHICS_EXPOSURES, (u32[]){X11_EXPOSURES_NOT_ALLOWED});

state.window = x11_init_window(&state, 0,0, WIDTH,HEIGHT, state.root_window, state.root_visual,
                               X11_CW_BACK_PIXEL | X11_CW_EVENT_MASK,
                               (u32[]){XCB_RGB_BLUE, X11_EVENT_MASK_KEY_PRESS | X11_EVENT_MASK_POINTER_MOTION});  // X11_EVENT_MASK_KEY_PRESS | X11_EVENT_MASK_POINTER_MOTION | X11_EVENT_MASK_STRUCTURE_NOTIFY | X11_EVENT_MASK_EXPOSURE
x11_map_window(&state, state.window);

Но сначала нам нужно его реализовать.


Для иллюстрации приведем первые тринадцать опкодов протокола X:

#define X11_OPCODE_CREATE_WINDOW                1
#define X11_OPCODE_CHANGE_WINDOW_ATTRIBUTES     2
#define X11_OPCODE_GET_WINDOW_ATTRIBUTES        3
#define X11_OPCODE_DESTROY_WINDOW               4
#define X11_OPCODE_DESTROY_SUBWINDOWS           5
#define X11_OPCODE_CHANGE_SAVE_SET              6
#define X11_OPCODE_REPARENT_WINDOW              7
#define X11_OPCODE_MAP_WINDOW                   8
#define X11_OPCODE_MAP_SUBWINDOWS               9
#define X11_OPCODE_UNMAP_WINDOW                10
#define X11_OPCODE_UNMAP_SUBWINDOWS            11
#define X11_OPCODE_CONFIGURE_WINDOW            12
#define X11_OPCODE_CIRCULATE_WINDOW            13

Вы можете открыть сокет с помощью следующей функции:

int x11_init_socket(){
  int socket_fd                = socket(AF_UNIX, SOCK_STREAM, 0);  // Create the socket!
  struct sockaddr_un serv_addr = {0};
  serv_addr.sun_family         = AF_UNIX;
  strcpy(serv_addr.sun_path, "/tmp/.X11-unix/X0");
  int srv_len                  = sizeof(struct sockaddr_un);
  connect(socket_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
  return socket_fd;
}

Тогда вам нужно рукопожатие,

int x11_init_connection(x11_connection_t* connection){
  connection->socket_fd = x11_init_socket();

  x11_connection_request_t request = {0};
  request.order = 'l';  // Little endian!
  request.major = 11;
  request.minor =  0;  // Version 11.0
  write(connection->socket_fd, &request, sizeof(x11_connection_request_t));             // Send request
  read(connection->socket_fd, &connection->header, sizeof(x11_connection_reply_t));     // Read reply header
  if(connection->header.success == 0)  return connection->header.success;   // Error handling!

  connection->setup = sbrk(connection->header.length * 4);       // Allocate memory for remainder of data
  read(connection->socket_fd, connection->setup, connection->header.length * 4);  // Read remainder of data

  void* p = ((void*)connection->setup);
                           p += sizeof(x11_connection_setup_t) + connection->setup->vendor_length;  // Ignore the vendor
  connection->format = p;  p += sizeof(x11_pixmap_format_t) * connection->setup->formats;   // Align struct with format sections. Move pointer to end of section
  connection->root   = p;  p += sizeof(x11_root_t) * connection->setup->roots;              // Align struct with root section(s). Move pointer to end of section
  connection->depth  = p;  p += sizeof(x11_depth_t);                                        // Align depth struct with first depth section. Move pointer to end of section
  connection->visual = p;  // Align visual with first visual for first depth

  return connection->header.success;
}

и куча структур данных, которые нравятся X-серверу:

typedef struct{
  u8  order;
  u8  pad1;
  u16 major, minor;
  u16 auth_proto, auth_data;
  u16 pad2;
}x11_connection_request_t;

typedef struct{
  u8  success;
  u8  pad1;
  u16 major, minor;
  u16 length;
}x11_connection_reply_t;

typedef struct{
  u32 release;
  u32 id_base, id_mask;
  u32 motion_buffer_size;
  u16 vendor_length;
  u16 request_max;
  u8  roots;
  u8  formats;
  u8  image_order;
  u8  bitmap_order;
  u8  scanline_unit, scanline_pad;
  u8  keycode_min, keycode_max;
  u32 pad;
}x11_connection_setup_t;

typedef struct{
  u8  depth;
  u8  bpp;
  u8  scanline_pad;
  u8  pad1;
  u32 pad2;
}x11_pixmap_format_t;

typedef struct{
  u32 window_id;
  u32 colormap;
  u32 white, black;
  u32 input_mask;   
  u16 width, height;
  u16 width_mm, height_mm;
  u16 maps_min, maps_max;
  u32 visual_id;
  u8  backing_store;
  u8  save_unders;
  u8  depth;
  u8  depths;
}x11_root_t;

typedef struct{
  u8  depth;
  u8  pad1;
  u16 visuals;
  u32 pad2;
}x11_depth_t;

typedef struct{
  u8  group;
  u8  bits;
  u16 colormap_entries;
  u32 mask_red, mask_green, mask_blue;
  u32 pad;
}x11_visual_t;

typedef struct{
  u8  response_type;  /**< Type of the response */
  u8  pad0;           /**< Padding */
  u16 sequence;       /**< Sequence number */
  u32 pad[7];         /**< Padding */
  u32 full_sequence;  /**< full sequence */
}x11_generic_event_t;

typedef struct{
  u8  success;
  u8  code;
  u16 seq;
  u32 id;
  u16 op_major;
  u8  op_minor;
  u8  pad[21];
}x11_error_t;

typedef struct{
  int socket_fd;
  x11_connection_reply_t  header;
  x11_connection_setup_t* setup;
  x11_pixmap_format_t*    format;
  x11_root_t*             root;
  x11_depth_t*            depth;
  x11_visual_t*           visual;
}x11_connection_t;

typedef struct{
  int  socket_fd;
  u32  id_base;  // We'll use this to generate 32-bit identifiers!

  u32  root_window;
  u32  root_visual;
  u8   root_depth;

  u32  window;
  u32  gcontext;
}x11_state_t;

Вам также нужна функция для генерации 32-битных идентификаторов:

u32 x11_generate_id(x11_connection_t* connection){
  static u32 id = 0;
  return (id++ | connection->setup->id_base);
}

Наконец, вам нужны реальные функции, которые собирают пакеты с запросами. Ниже приведены вспомогательные функции для отправки X11_OPCODE_CREATE_GC, X11_OPCODE_CREATE_WINDOW, X11_OPCODE_MAP_WINDOW и X11_OPCODE_PUT_IMAGE.

#define X11_NONE             0x00000000L  // X11_NONE is the universal null resource or null atom parameter value for many core X requests
#define X11_COPY_FROM_PARENT 0x00000000L  // X11_COPY_FROM_PARENT can be used for many xcb_create_window parameters
#define X11_CURRENT_TIME     0x00000000L  // X11_CURRENT_TIME can be used in most requests that take an xcb_timestamp_t
#define X11_NO_SYMBOL        0x00000000L  // X11_NO_SYMBOL fills in unused entries in xcb_keysym_t tables

enum x11_exposures_t{
  X11_EXPOSURES_NOT_ALLOWED = 0,
  X11_EXPOSURES_ALLOWED     = 1,
  X11_EXPOSURES_DEFAULT     = 2
};

enum x11_gc_t{
  X11_GC_FUNCTION              = 1<<0,
  X11_GC_PLANE_MASK            = 1<<1,
  X11_GC_FOREGROUND            = 1<<2,
  X11_GC_BACKGROUND            = 1<<3,
  X11_GC_LINE_WIDTH            = 1<<4,
  X11_GC_LINE_STYLE            = 1<<5,
  X11_GC_CAP_STYLE             = 1<<6,
  X11_GC_JOIN_STYLE            = 1<<7,
  X11_GC_FILL_STYLE            = 1<<8,
  X11_GC_FILL_RULE             = 1<<9,
  X11_GC_TILE                  = 1<<10,
  X11_GC_STIPPLE               = 1<<11,
  X11_GC_TILE_STIPPLE_ORIGIN_X = 1<<12,
  X11_GC_TILE_STIPPLE_ORIGIN_Y = 1<<13,
  X11_GC_FONT                  = 1<<14,
  X11_GC_SUBWINDOW_MODE        = 1<<15,
  X11_GC_GRAPHICS_EXPOSURES    = 1<<16,
  X11_GC_CLIP_ORIGIN_X         = 1<<17,
  X11_GC_CLIP_ORIGIN_Y         = 1<<18,
  X11_GC_CLIP_MASK             = 1<<19,
  X11_GC_DASH_OFFSET           = 1<<20,
  X11_GC_DASH_LIST             = 1<<21,
  X11_GC_ARC_MODE              = 1<<22,
};

u32 x11_init_gc(x11_state_t* state, u32 value_mask, u32* value_list){
  u32 gcontext_id = x11_generate_id(state);

  u16 flag_count = popcnt(value_mask);
  u16 length     = 4 + flag_count;
  u32* packet    = sbrk(length * 4);

  packet[0] = X11_OPCODE_CREATE_GC | (length<<16);  // The first `u32` in the packet is always the opcode and the length of the packet!
  packet[1] = gcontext_id;
  packet[2] = state->root_window;
  packet[3] = value_mask;
  for(int i=0; i < flag_count; ++i)
    packet[4 + i] = value_list[i];

  write(state->socket_fd, packet, length * 4);
  sbrk(-(length * 4));

  return gcontext_id;
}

enum x11_cw_t{
  X11_CW_BACK_PIXMAP       = 1<<0,
  X11_CW_BACK_PIXEL        = 1<<1,
  X11_CW_BORDER_PIXMAP     = 1<<2,
  X11_CW_BORDER_PIXEL      = 1<<3,
  X11_CW_BIT_GRAVITY       = 1<<4,
  X11_CW_WIN_GRAVITY       = 1<<5,
  X11_CW_BACKING_STORE     = 1<<6,
  X11_CW_BACKING_PLANES    = 1<<7,
  X11_CW_BACKING_PIXEL     = 1<<8,
  X11_CW_OVERRIDE_REDIRECT = 1<<9,
  X11_CW_SAVE_UNDER        = 1<<10,
  X11_CW_EVENT_MASK        = 1<<11,
  X11_CW_DONT_PROPAGATE    = 1<<12,
  X11_CW_COLORMAP          = 1<<13,
  X11_CW_CURSOR            = 1<<14
};

enum x11_event_mask_t{
  X11_EVENT_MASK_NO_EVENT               = 0,
  X11_EVENT_MASK_KEY_PRESS              = 1<<0,   // 0x00000001
  X11_EVENT_MASK_KEY_RELEASE            = 1<<1,   // 0x00000002
  X11_EVENT_MASK_BUTTON_PRESS           = 1<<2,   // 0x00000004
  X11_EVENT_MASK_BUTTON_RELEASE         = 1<<3,   // 0x00000008
  X11_EVENT_MASK_ENTER_WINDOW           = 1<<4,   // 0x00000010  
  X11_EVENT_MASK_LEAVE_WINDOW           = 1<<5,   // 0x00000020  
  X11_EVENT_MASK_POINTER_MOTION         = 1<<6,   // 0x00000040  
  X11_EVENT_MASK_POINTER_MOTION_HINT    = 1<<7,   // 0x00000080  
  X11_EVENT_MASK_BUTTON_1_MOTION        = 1<<8,   // 0x00000100  
  X11_EVENT_MASK_BUTTON_2_MOTION        = 1<<9,   // 0x00000200  
  X11_EVENT_MASK_BUTTON_3_MOTION        = 1<<10,  // 0x00000400  
  X11_EVENT_MASK_BUTTON_4_MOTION        = 1<<11,  // 0x00000800  
  X11_EVENT_MASK_BUTTON_5_MOTION        = 1<<12,  // 0x00001000  
  X11_EVENT_MASK_BUTTON_MOTION          = 1<<13,  // 0x00002000  
  X11_EVENT_MASK_KEYMAP_STATE           = 1<<14,  // 0x00004000  
  X11_EVENT_MASK_EXPOSURE               = 1<<15,  // 0x00008000  
  X11_EVENT_MASK_VISIBILITY_CHANGE      = 1<<16,  // 0x00010000  
  X11_EVENT_MASK_STRUCTURE_NOTIFY       = 1<<17,  // 0x00020000  
  X11_EVENT_MASK_RESIZE_REDIRECT        = 1<<18,  // 0x00040000  
  X11_EVENT_MASK_SUBSTRUCTURE_NOTIFY    = 1<<19,  // 0x00080000  
  X11_EVENT_MASK_SUBSTRUCTURE_REDIRECT  = 1<<20,  // 0x00100000  
  X11_EVENT_MASK_FOCUS_CHANGE           = 1<<21,  // 0x00200000  
  X11_EVENT_MASK_PROPERTY_CHANGE        = 1<<22,  // 0x00400000  
  X11_EVENT_MASK_COLOR_MAP_CHANGE       = 1<<23,  // 0x00800000  
  X11_EVENT_MASK_OWNER_GRAB_BUTTON      = 1<<24   // 0x01000000
};

#define X11_DEFAULT_BORDER 0
#define X11_DEFAULT_GROUP  0

u32 x11_init_window(x11_state_t* state, u16 x, u16 y, u16 w, u16 h, u32 window_parent, u32 visual, u32 value_mask, u32* value_list){
  u32 window_id = x11_generate_id(state);

  u16 flag_count = popcnt(value_mask);
  u16 length     = 8 + flag_count;
  u32* packet    = sbrk(length * 4);

  packet[0] = X11_OPCODE_CREATE_WINDOW | length<<16;  // The first `u32` in the packet is always the opcode and the length of the packet!
  packet[1] = window_id;
  packet[2] = window_parent;
  packet[3] = x | (y<<16);
  packet[4] = w | (h<<16);
  packet[5] = (X11_DEFAULT_BORDER<<16) | X11_DEFAULT_GROUP;
  packet[6] = visual;
  packet[7] = value_mask;
  for(int i=0;i < flag_count; ++i)
    packet[8 + i] = value_list[i];

  write(state->socket_fd, packet, length * 4);
  sbrk(-(length * 4));

  return window_id;
}

void x11_map_window(x11_state_t* state, u32 window_id){
  int const length = 2;
  u32 packet[length];
  packet[0] = X11_OPCODE_MAP_WINDOW | (length<<16);  // The first `u32` in the packet is always the opcode and the length of the packet!
  packet[1] = window_id;
  write(state->socket_fd, packet, 8);
}

enum x11_image_format_t{
  X11_IMAGE_FORMAT_XY_BITMAP = 0,
  X11_IMAGE_FORMAT_XY_PIXMAP = 1,
  X11_IMAGE_FORMAT_Z_PIXMAP  = 2
};

void x11_put_img(x11_state_t* state, u16 x, u16 y, u32* data){
  u32 packet[6];
  u16 const length = ((state->tile_width * state->tile_height)) + 6;

  packet[0] = X11_OPCODE_PUT_IMAGE | (X11_IMAGE_FORMAT_Z_PIXMAP<<8) | (length<<16);  // The first `u32` in the packet is always the opcode and the length of the packet!
  packet[1] = state->window;
  packet[2] = state->gcontext;
  packet[3] = state->tile_width | (state->tile_height<<16);
  packet[4] = x | (y<<16);
  packet[5] = state->root_depth<<8;

  write(state->socket_fd, packet, 24);
  write(state->socket_fd, data, state->tile_width * state->tile_height * 4);
}
person étale-cohomology    schedule 13.05.2018
comment
У вас есть код сборки и запуска, которым вы могли бы поделиться? tile_width и XCB_RGB_BLUE используются, но никогда не определяются. - person Andrew Bainbridge; 25.05.2020
comment
XCB_RGB_BLUE — это всего лишь uint32 цвет, например 0x0099ff. - person étale-cohomology; 27.05.2020
comment
@AndrewBainbridge кстати, у меня есть код, который компилируется и запускается (хотя он ... сырой). Как я могу отправить его вам - person étale-cohomology; 12.06.2020
comment
Спасибо, но теперь у меня работает собственный код. Это также немного сыро. Я разместил копию здесь: github.com/abainbridge/x11_socket. Я упомянул вас в ридми. Дайте мне знать, если вы хотите удалить кредит! - person Andrew Bainbridge; 12.06.2020

Я сделал это с помощью node/javascript ( https://github.com/sidorares/node-x11/ ) Это не так страшно, как кажется, и для некоторых конкретных задач может работать лучше, чем xlib (нетривиальный параллелизм или зависимость от ресурсов или косвенная glx ), но, вероятно, большая часть ценности в этом образовательная. Если вы просто хотите, чтобы какая-то работа была выполнена, вероятно, есть доступное решение, и вам не нужно изобретать велосипед.

Отправной точкой будет документация по протоколу x11: https://www.x.org/releases/X11R7.7/doc/xproto/x11protocol.html

person Andrey Sidorov    schedule 15.05.2018