BetterOS.org : An attempt to make computer machines run better

Graphics Tutorial



Low-Level Graphics on Linux



Tutorial 1: Intro to Low-Level Graphics on Linux

Introduction

Contents

Methods for Rendering in Linux

Linux Framebuffer Device (fbdev)

int fb_fd = open("/dev/fb0",O_RDWR);

#include <linux/fb.h> ... struct fb_fix_screeninfo finfo; struct fb_var_screeninfo vinfo;

struct fb_fix_screeninfo { char id[16]; /* identification string eg "TT Builtin" */ unsigned long smem_start; /* Start of frame buffer mem */ /* (physical address) */ __u32 smem_len; /* Length of frame buffer mem */ __u32 type; /* see FB_TYPE_* */ __u32 type_aux; /* Interleave for interleaved Planes */ __u32 visual; /* see FB_VISUAL_* */ __u16 xpanstep; /* zero if no hardware panning */ __u16 ypanstep; /* zero if no hardware panning */ __u16 ywrapstep; /* zero if no hardware ywrap */ __u32 line_length; /* length of a line in bytes */ unsigned long mmio_start; /* Start of Memory Mapped I/O */ /* (physical address) */ __u32 mmio_len; /* Length of Memory Mapped I/O */ __u32 accel; /* Indicate to driver which */ /* specific chip/card we have */ __u16 capabilities; /* see FB_CAP_* */ __u16 reserved[2]; /* Reserved for future compatibility */ }; ... struct fb_var_screeninfo { __u32 xres; /* visible resolution */ __u32 yres; __u32 xres_virtual; /* virtual resolution */ __u32 yres_virtual; __u32 xoffset; /* offset from virtual to visible */ __u32 yoffset; /* resolution */ __u32 bits_per_pixel; /* guess what */ __u32 grayscale; /* 0 = color, 1 = grayscale, */ /* >1 = FOURCC */ struct fb_bitfield red; /* bitfield in fb mem if true color, */ struct fb_bitfield green; /* else only length is significant */ struct fb_bitfield blue; struct fb_bitfield transp; /* transparency */ __u32 nonstd; /* != 0 Non standard pixel format */ __u32 activate; /* see FB_ACTIVATE_* */ __u32 height; /* height of picture in mm */ __u32 width; /* width of picture in mm */ __u32 accel_flags; /* (OBSOLETE) see fb_info.flags */ /* Timing: All values in pixclocks, except pixclock (of course) */ __u32 pixclock; /* pixel clock in ps (pico seconds) */ __u32 left_margin; /* time from sync to picture */ __u32 right_margin; /* time from picture to sync */ __u32 upper_margin; /* time from sync to picture */ __u32 lower_margin; __u32 hsync_len; /* length of horizontal sync */ __u32 vsync_len; /* length of vertical sync */ __u32 sync; /* see FB_SYNC_* */ __u32 vmode; /* see FB_VMODE_* */ __u32 rotate; /* angle we rotate counter clockwise */ __u32 colorspace; /* colorspace for FOURCC-based modes */ __u32 reserved[4]; /* Reserved for future compatibility */ };

//Get variable screen information ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo); //Get fixed screen information ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo);

//Get variable screen information ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo); vinfo.grayscale=0; vinfo.bits_per_pixel=32; ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vinfo); ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo);

long screensize = vinfo.yres_virtual * finfo.line_length;

uint8_t *fbp = mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, (off_t)0);

long x,y; //location we want to draw the pixel uint32_t pixel; //The pixel we want to draw at that location //Make sure you set x,y and pixel correctly (details later) long location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y+vinfo.yoffset) * finfo.line_length; *((uint32_t*)(fbp + location)) = pixel;

(y+vinfo.yoffset) * finfo.line_length

(x+vinfo.xoffset) * (vinfo.bits_per_pixel/8)

inline uint32_t pixel_color(uint8_t r, uint8_t g, uint8_t b, struct fb_var_screeninfo *vinfo) { return (r<<vinfo->red.offset) | (g<<vinfo->green.offset) | (b<<vinfo->blue.offset); }

vinfo.xres

vinfo.yres

#include <linux/fb.h> #include <stdio.h> #include <stdint.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/ioctl.h> inline uint32_t pixel_color(uint8_t r, uint8_t g, uint8_t b, struct fb_var_screeninfo *vinfo) { return (r<<vinfo->red.offset) | (g<<vinfo->green.offset) | (b<<vinfo->blue.offset); } int main() { struct fb_fix_screeninfo finfo; struct fb_var_screeninfo vinfo; int fb_fd = open("/dev/fb0",O_RDWR); //Get variable screen information ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo); vinfo.grayscale=0; vinfo.bits_per_pixel=32; ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vinfo); ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo); ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo); long screensize = vinfo.yres_virtual * finfo.line_length; uint8_t *fbp = mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, (off_t)0); int x,y; for (x=0;x<vinfo.xres;x++) for (y=0;y<vinfo.yres;y++) { long location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y+vinfo.yoffset) * finfo.line_length; *((uint32_t*)(fbp + location)) = pixel_color(0xFF,0x00,0xFF, &vinfo); } return 0; }

int tty_fd = open("/dev/tty0", O_RDWR); ioctl(tty_fd,KDSETMODE,KD_GRAPHICS); ... //At exit: ioctl(tty_fd,KDSETMODE,KD_TEXT);

Direct Rendering Manager (DRM) Dumb Buffers

int dri_fd = open("/dev/dri/card0",O_RDWR);

#include <drm/drm.h> #include <drm/drm_mode.h>

ioctl(dri_fd, DRM_IOCTL_SET_MASTER, 0);

struct drm_mode_card_res { __u64 fb_id_ptr; __u64 crtc_id_ptr; __u64 connector_id_ptr; __u64 encoder_id_ptr; __u32 count_fbs; __u32 count_crtcs; __u32 count_connectors; __u32 count_encoders; __u32 min_width, max_width; __u32 min_height, max_height; };

uint64_t res_fb_buf[10]={0}, res_crtc_buf[10]={0}, res_conn_buf[10]={0}, res_enc_buf[10]={0}; struct drm_mode_card_res res={0}; //Get resource counts ioctl(dri_fd, DRM_IOCTL_MODE_GETRESOURCES, &res); res.fb_id_ptr=(uint64_t)res_fb_buf; res.crtc_id_ptr=(uint64_t)res_crtc_buf; res.connector_id_ptr=(uint64_t)res_conn_buf; res.encoder_id_ptr=(uint64_t)res_enc_buf; //Get resource IDs ioctl(dri_fd, DRM_IOCTL_MODE_GETRESOURCES, &res);

int i; for (i=0;i<res.count_connectors;i++) {

struct drm_mode_get_connector { __u64 encoders_ptr; __u64 modes_ptr; __u64 props_ptr; __u64 prop_values_ptr; __u32 count_modes; __u32 count_props; __u32 count_encoders; __u32 encoder_id; /** Current Encoder */ __u32 connector_id; /** Id */ __u32 connector_type; __u32 connector_type_id; __u32 connection; __u32 mm_width, mm_height; /** HxW in millimeters */ __u32 subpixel; };

struct drm_mode_modeinfo { __u32 clock; __u16 hdisplay, hsync_start, hsync_end, htotal, hskew; __u16 vdisplay, vsync_start, vsync_end, vtotal, vscan; __u32 vrefresh; __u32 flags; __u32 type; char name[DRM_DISPLAY_MODE_LEN]; };

struct drm_mode_modeinfo conn_mode_buf[20]={0}; uint64_t conn_prop_buf[20]={0}, conn_propval_buf[20]={0}, conn_enc_buf[20]={0}; struct drm_mode_get_connector conn={0}; conn.connector_id=res_conn_buf[i]; ioctl(dri_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn); //get connector resource counts conn.modes_ptr=(uint64_t)conn_mode_buf; conn.props_ptr=(uint64_t)conn_prop_buf; conn.prop_values_ptr=(uint64_t)conn_propval_buf; conn.encoders_ptr=(uint64_t)conn_enc_buf; ioctl(dri_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn); //get connector resource IDs

if (conn.count_encoders<1 || conn.count_modes<1 || !conn.encoder_id || !conn.connection) continue;

void *fb_base[10]; long fb_w[10]; long fb_h[10];

struct drm_mode_create_dumb create_dumb={0}; struct drm_mode_map_dumb map_dumb={0}; struct drm_mode_fb_cmd cmd_dumb={0};

struct drm_mode_create_dumb { __u32 height; __u32 width; __u32 bpp; __u32 flags; __u32 handle; __u32 pitch; __u64 size; }; struct drm_mode_fb_cmd { __u32 fb_id; __u32 width, height; __u32 pitch; __u32 bpp; __u32 depth; /* driver specific handle */ __u32 handle; }; struct drm_mode_map_dumb { __u32 handle; __u32 pad; __u64 offset; };

create_dumb.width = conn_mode_buf[0].hdisplay; create_dumb.height = conn_mode_buf[0].vdisplay; create_dumb.bpp = 32; create_dumb.flags = 0; create_dumb.pitch = 0; create_dumb.size = 0; create_dumb.handle = 0; ioctl(dri_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb); cmd_dumb.width=create_dumb.width; cmd_dumb.height=create_dumb.height; cmd_dumb.bpp=create_dumb.bpp; cmd_dumb.pitch=create_dumb.pitch; cmd_dumb.depth=24; cmd_dumb.handle=create_dumb.handle; ioctl(dri_fd,DRM_IOCTL_MODE_ADDFB,&cmd_dumb); map_dumb.handle=create_dumb.handle; ioctl(dri_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb); fb_base[i] = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED, dri_fd, map_dumb.offset); fb_w[i]=create_dumb.width; fb_h[i]=create_dumb.height;

struct drm_mode_get_encoder { __u32 encoder_id; __u32 encoder_type; __u32 crtc_id; /** Id of crtc */ __u32 possible_crtcs; __u32 possible_clones; };

struct drm_mode_crtc { __u64 set_connectors_ptr; __u32 count_connectors; __u32 crtc_id; /** Id */ __u32 fb_id; /** Id of framebuffer */ __u32 x, y; /** Position on the frameuffer */ __u32 gamma_size; __u32 mode_valid; struct drm_mode_modeinfo mode; };

struct drm_mode_get_encoder enc={0}; enc.encoder_id=conn.encoder_id; ioctl(dri_fd, DRM_IOCTL_MODE_GETENCODER, &enc); //get encoder struct drm_mode_crtc crtc={0}; crtc.crtc_id=enc.crtc_id; ioctl(dri_fd, DRM_IOCTL_MODE_GETCRTC, &crtc);

crtc.fb_id=cmd_dumb.fb_id; crtc.set_connectors_ptr=(uint64_t)&res_conn_buf[i]; crtc.count_connectors=1; crtc.mode=conn_mode_buf[0]; crtc.mode_valid=1; ioctl(dri_fd, DRM_IOCTL_MODE_SETCRTC, &crtc); }

ioctl(dri_fd, DRM_IOCTL_DROP_MASTER, 0);

int i; for (i=0;i<res.count_connectors;i++) for (y=0;y<fb_h[i];y++) for (x=0;x<fb_w[i];x++) { int location=y*(fb_w[i]) + x; *(((uint32_t*)fb_base[i])+location)=0x00ff00ff; }

#include <stdio.h> #include <stdint.h< #include <fcntl.h< #include <sys/mman.h< #include <sys/ioctl.h< #include <drm/drm.h< #include <drm/drm_mode.h< int main() { //------------------------------------------------------------------------------ //Opening the DRI device //------------------------------------------------------------------------------ int dri_fd = open("/dev/dri/card0",O_RDWR | O_CLOEXEC); //------------------------------------------------------------------------------ //Kernel Mode Setting (KMS) //------------------------------------------------------------------------------ uint64_t res_fb_buf[10]={0}, res_crtc_buf[10]={0}, res_conn_buf[10]={0}, res_enc_buf[10]={0}; struct drm_mode_card_res res={0}; //Become the "master" of the DRI device ioctl(dri_fd, DRM_IOCTL_SET_MASTER, 0); //Get resource counts ioctl(dri_fd, DRM_IOCTL_MODE_GETRESOURCES, &res); res.fb_id_ptr=(uint64_t)res_fb_buf; res.crtc_id_ptr=(uint64_t)res_crtc_buf; res.connector_id_ptr=(uint64_t)res_conn_buf; res.encoder_id_ptr=(uint64_t)res_enc_buf; //Get resource IDs ioctl(dri_fd, DRM_IOCTL_MODE_GETRESOURCES, &res); printf("fb: %d, crtc: %d, conn: %d, enc: %d

",res.count_fbs,res.count_crtcs,res.count_connectors,res.count_encoders); void *fb_base[10]; long fb_w[10]; long fb_h[10]; //Loop though all available connectors int i; for (i=0;i<res.count_connectors;i++) { struct drm_mode_modeinfo conn_mode_buf[20]={0}; uint64_t conn_prop_buf[20]={0}, conn_propval_buf[20]={0}, conn_enc_buf[20]={0}; struct drm_mode_get_connector conn={0}; conn.connector_id=res_conn_buf[i]; ioctl(dri_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn); //get connector resource counts conn.modes_ptr=(uint64_t)conn_mode_buf; conn.props_ptr=(uint64_t)conn_prop_buf; conn.prop_values_ptr=(uint64_t)conn_propval_buf; conn.encoders_ptr=(uint64_t)conn_enc_buf; ioctl(dri_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn); //get connector resources //Check if the connector is OK to use (connected to something) if (conn.count_encoders<1 || conn.count_modes<1 || !conn.encoder_id || !conn.connection) { printf("Not connected

"); continue; } //------------------------------------------------------------------------------ //Creating a dumb buffer //------------------------------------------------------------------------------ struct drm_mode_create_dumb create_dumb={0}; struct drm_mode_map_dumb map_dumb={0}; struct drm_mode_fb_cmd cmd_dumb={0}; //If we create the buffer later, we can get the size of the screen first. //This must be a valid mode, so it's probably best to do this after we find //a valid crtc with modes. create_dumb.width = conn_mode_buf[0].hdisplay; create_dumb.height = conn_mode_buf[0].vdisplay; create_dumb.bpp = 32; create_dumb.flags = 0; create_dumb.pitch = 0; create_dumb.size = 0; create_dumb.handle = 0; ioctl(dri_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb); cmd_dumb.width=create_dumb.width; cmd_dumb.height=create_dumb.height; cmd_dumb.bpp=create_dumb.bpp; cmd_dumb.pitch=create_dumb.pitch; cmd_dumb.depth=24; cmd_dumb.handle=create_dumb.handle; ioctl(dri_fd,DRM_IOCTL_MODE_ADDFB,&cmd_dumb); map_dumb.handle=create_dumb.handle; ioctl(dri_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb); fb_base[i] = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED, dri_fd, map_dumb.offset); fb_w[i]=create_dumb.width; fb_h[i]=create_dumb.height; //------------------------------------------------------------------------------ //Kernel Mode Setting (KMS) //------------------------------------------------------------------------------ printf("%d : mode: %d, prop: %d, enc: %d

",conn.connection,conn.count_modes,conn.count_props,conn.count_encoders); printf("modes: %dx%d FB: %d

",conn_mode_buf[0].hdisplay,conn_mode_buf[0].vdisplay,fb_base[i]); struct drm_mode_get_encoder enc={0}; enc.encoder_id=conn.encoder_id; ioctl(dri_fd, DRM_IOCTL_MODE_GETENCODER, &enc); //get encoder struct drm_mode_crtc crtc={0}; crtc.crtc_id=enc.crtc_id; ioctl(dri_fd, DRM_IOCTL_MODE_GETCRTC, &crtc); crtc.fb_id=cmd_dumb.fb_id; crtc.set_connectors_ptr=(uint64_t)&res_conn_buf[i]; crtc.count_connectors=1; crtc.mode=conn_mode_buf[0]; crtc.mode_valid=1; ioctl(dri_fd, DRM_IOCTL_MODE_SETCRTC, &crtc); } //Stop being the "master" of the DRI device ioctl(dri_fd, DRM_IOCTL_DROP_MASTER, 0); int x,y; for (i=0;i<100;i++) { int j; for (j=0;j<res.count_connectors;j++) { int col=(rand()%0x00ffffff)&0x00ff00ff; for (y=0;y<fb_h[j];y++) for (x=0;x<fb_w[j];x++) { int location=y*(fb_w[j]) + x; *(((uint32_t*)fb_base[j])+location)=col; } } usleep(100000); } return 0; }

X Server (X11) Direct Connection

network

socket

host:display.screen

//Create the socket sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd < 0) error("Error opening socket

",21); serv_addr.sun_family = AF_UNIX; strcpy(serv_addr.sun_path, "/tmp/.X11-unix/X0"); srv_len = sizeof(struct sockaddr_un); //Connect to socket connect(sockfd,(struct sockaddr *)&serv_addr,srv_len);

struct x11_conn_req { uint8_t order; uint8_t pad1; uint16_t major, minor; uint16_t auth_proto, auth_data; uint16_t pad2; };

struct x11_conn_reply { uint8_t success; uint8_t pad1; uint16_t major, minor; uint16_t length; };

struct x11_conn_setup { uint32_t release; uint32_t id_base, id_mask; uint32_t motion_buffer_size; uint16_t vendor_length; uint16_t request_max; uint8_t roots; uint8_t formats; uint8_t image_order; uint8_t bitmap_order; uint8_t scanline_unit, scanline_pad; uint8_t keycode_min, keycode_max; uint32_t pad; };

struct x11_pixmap_format { uint8_t depth; uint8_t bpp; uint8_t scanline_pad; uint8_t pad1; uint32_t pad2; };

struct x11_root_window { uint32_t id; uint32_t colormap; uint32_t white, black; uint32_t input_mask; uint16_t width, height; uint16_t width_mm, height_mm; uint16_t maps_min, maps_max; uint32_t root_visual_iD; uint8_t backing_store; uint8_t save_unders; uint8_t depth; uint8_t depths; };

struct x11_depth { uint8_t depth; uint8_t pad1; uint16_t visuals; uint32_t pad2; };

struct x11_visual { uint8_t group; uint8_t bits; uint16_t colormap_entries; uint32_t mask_red, mask_green, mask_blue; uint32_t pad; };

int x11_handshake(int sock, struct x11_connection *conn) { struct x11_conn_req req = {0}; req.order='l'; //Little endian req.major=11; req.minor=0; //Version 11.0 write(sock,&req,sizeof(struct x11_conn_req)); //Send request read(sock,&conn->header,sizeof(struct x11_conn_reply)); //Read reply header if (conn->header.success==0) return conn->header.success; conn->setup = sbrk(conn->header.length*4); //Allocate memory for remainder of data read(sock,conn->setup,conn->header.length*4); //Read remainder of data void* p = ((void*)conn->setup)+sizeof(struct x11_conn_setup)+conn->setup->vendor_length; //Ignore the vendor conn->format = p; //Align struct with format sections p += sizeof(struct x11_pixmap_format)*conn->setup->formats; //move pointer to end of section conn->root = p; //Align struct with root section(s) p += sizeof(struct x11_root_window)//move pointer to end of section conn->depth = p; //Align depth struct with first depth section p += sizeof(struct x11_depth); //move pointer to end of section conn->visual = p; //Align visual with first visual for first depth return conn->header.success; }

uint32_t x11_generate_id(struct x11_connection *conn) { static uint32_t id = 0; return (conn->setup->id_mask & id++ | conn->setup->id_base); }

#define X11_OP_REQ_CREATE_WINDOW 0x01 #define X11_OP_REQ_MAP_WINDOW 0x08 #define X11_OP_REQ_CREATE_GC 0x37

//MIT HACKMEM bit counting algorithm int count_bits(uint32_t n) { unsigned int c; c = n - ((n >> 1) & 0x55555555); c = ((c >> 2) & 0x33333333) + (c & 0x33333333); c = ((c >> 4) + c) & 0x0F0F0F0F; c = ((c >> 8) + c) & 0x00FF00FF; c = ((c >> 16) + c) & 0x0000FFFF; return c; }

#define X11_FLAG_GC_FUNC 0x00000001 #define X11_FLAG_GC_PLANE 0x00000002 #define X11_FLAG_GC_BG 0x00000004 #define X11_FLAG_GC_FG 0x00000008 #define X11_FLAG_GC_LINE_WIDTH 0x00000010 #define X11_FLAG_GC_LINE_STYLE 0x00000020 #define X11_FLAG_GC_FONT 0x00004000 ... #define X11_FLAG_WIN_BG_IMG 0x00000001 #define X11_FLAG_WIN_BG_COLOR 0x00000002 #define X11_FLAG_WIN_BORDER_IMG 0x00000004 #define X11_FLAG_WIN_BORDER_COLOR 0x00000008 #define X11_FLAG_WIN_EVENT 0x00000800

void x11_create_gc(int sock, struct x11_connection *conn, uint32_t id, uint32_t target, uint32_t flags, uint32_t *list) { uint16_t flag_count = count_bits(flags); uint16_t length = 4 + flag_count; uint32_t *packet = sbrk(length*4); packet[0]=X11_OP_REQ_CREATE_GC | length<<16; packet[1]=id; packet[2]=target; packet[3]=flags; int i; for (i=0;i<flag_count;i++) packet[4+i] = list[i]; write(sock,packet,length*4); sbrk(-(length*4)); return; }

void x11_create_win(int sock, struct x11_connection *conn, uint32_t id, uint32_t parent, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t border, uint16_t group, uint32_t visual, uint32_t flags, uint32_t *list) { uint16_t flag_count = count_bits(flags); uint16_t length = 8 + flag_count; uint32_t *packet = sbrk(length*4); packet[0]=X11_OP_REQ_CREATE_WINDOW | length<<16; packet[1]=id; packet[2]=parent; packet[3]=x | y<<16; packet[4]=w | h<<16; packet[5]=border<<16 | group; packet[6]=visual; packet[7]=flags; int i; for (i=0;i<flag_count;i++) packet[8+i] = list[i]; write(sock,packet,length*4); sbrk(-(length*4)); return; }

void x11_map_window(int sock, struct x11_connection *conn, uint32_t id) { uint32_t packet[2]; packet[0]=X11_OP_REQ_MAP_WINDOW | 2<<16; packet[1]=id; write(sock,packet,8); return; }

#define X11_IMG_FORMAT_MONO 0x0 #define X11_IMG_FORMAT_XY 0x01 #define X11_IMG_FORMAT_Z 0x02 void x11_put_img(int sock, struct x11_connection *conn, uint8_t format, uint32_t target, uint32_t gc, uint16_t w, uint16_t h, uint16_t x, uint16_t y, uint8_t depth, void *data) { uint32_t packet[6]; uint16_t length = ((w * h)) + 6; packet[0]=X11_OP_REQ_PUT_IMG | format<<8 | length<<16; packet[1]=target; packet[2]=gc; packet[3]=w | h<<16; packet[4]=x | y<<16; packet[5]=depth<<8; write(sock,packet,24); write(sock,data,(w*h)*4); return; }

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> //O_RDONLY #include <sys/types.h> //uint64_t #include <sys/socket.h> //Socket related constants #include <sys/un.h> //Unix domain constants #include <netinet/in.h> //Socket related constants #ifdef __linux__ #include <linux/limits.h> //PATH_MAX #else #define PATH_MAX 4096 #endif #define X11_OP_REQ_CREATE_WINDOW 0x01 #define X11_OP_REQ_MAP_WINDOW 0x08 #define X11_OP_REQ_CREATE_PIX 0x35 #define X11_OP_REQ_CREATE_GC 0x37 #define X11_OP_REQ_PUT_IMG 0x48 struct x11_conn_req { uint8_t order; uint8_t pad1; uint16_t major, minor; uint16_t auth_proto, auth_data; uint16_t pad2; }; struct x11_conn_reply { uint8_t success; uint8_t pad1; uint16_t major, minor; uint16_t length; }; struct x11_conn_setup { uint32_t release; uint32_t id_base, id_mask; uint32_t motion_buffer_size; uint16_t vendor_length; uint16_t request_max; uint8_t roots; uint8_t formats; uint8_t image_order; uint8_t bitmap_order; uint8_t scanline_unit, scanline_pad; uint8_t keycode_min, keycode_max; uint32_t pad; }; struct x11_pixmap_format { uint8_t depth; uint8_t bpp; uint8_t scanline_pad; uint8_t pad1; uint32_t pad2; }; struct x11_root_window { uint32_t id; uint32_t colormap; uint32_t white, black; uint32_t input_mask; uint16_t width, height; uint16_t width_mm, height_mm; uint16_t maps_min, maps_max; uint32_t root_visual_id; uint8_t backing_store; uint8_t save_unders; uint8_t depth; uint8_t depths; }; struct x11_depth { uint8_t depth; uint8_t pad1; uint16_t visuals; uint32_t pad2; }; struct x11_visual { uint8_t group; uint8_t bits; uint16_t colormap_entries; uint32_t mask_red, mask_green, mask_blue; uint32_t pad; }; struct x11_connection { struct x11_conn_reply header; struct x11_conn_setup *setup; struct x11_pixmap_format *format; struct x11_root_window *root; struct x11_depth *depth; struct x11_visual *visual; }; struct x11_error { uint8_t success; uint8_t code; uint16_t seq; uint32_t id; uint16_t op_major; uint8_t op_minor; uint8_t pad[21]; }; //Copy characters from one string to another until end int strcopy(char *dest, const char *src, char end) { int i; for (i=0; src[i]!=end; i++) dest[i]=src[i]; return i; } //Copy n characters from one string to another char *strncopy(char *dest, const char *src, size_t n) { int i; for (i=0; i<n; i++) dest[i]=src[i]; return dest; } int count_bits(uint32_t n) { unsigned int c; c = n - ((n >> 1) & 0x55555555); c = ((c >> 2) & 0x33333333) + (c & 0x33333333); c = ((c >> 4) + c) & 0x0F0F0F0F; c = ((c >> 8) + c) & 0x00FF00FF; c = ((c >> 16) + c) & 0x0000FFFF; return c; } void error(char *msg, int len) { write(0, msg, len); exit(1); return; } int x11_handshake(int sock, struct x11_connection *conn) { struct x11_conn_req req = {0}; req.order='l'; //Little endian req.major=11; req.minor=0; //Version 11.0 write(sock,&req,sizeof(struct x11_conn_req)); //Send request read(sock,&conn->header,sizeof(struct x11_conn_reply)); //Read reply header if (conn->header.success==0) return conn->header.success; conn->setup = sbrk(conn->header.length*4); //Allocate memory for remainder of data read(sock,conn->setup,conn->header.length*4); //Read remainder of data void* p = ((void*)conn->setup)+sizeof(struct x11_conn_setup)+conn->setup->vendor_length; //Ignore the vendor conn->format = p; //Align struct with format sections p += sizeof(struct x11_pixmap_format)*conn->setup->formats; //move pointer to end of section conn->root = p; //Align struct with root section(s) p += sizeof(struct x11_root_window)*conn->setup->roots; //move pointer to end of section conn->depth = p; //Align depth struct with first depth section p += sizeof(struct x11_depth); //move pointer to end of section conn->visual = p; //Align visual with first visual for first depth return conn->header.success; } uint32_t x11_generate_id(struct x11_connection *conn) { static uint32_t id = 0; return (id++ | conn->setup->id_base); } #define X11_FLAG_GC_FUNC 0x00000001 #define X11_FLAG_GC_PLANE 0x00000002 #define X11_FLAG_GC_BG 0x00000004 #define X11_FLAG_GC_FG 0x00000008 #define X11_FLAG_GC_LINE_WIDTH 0x00000010 #define X11_FLAG_GC_LINE_STYLE 0x00000020 #define X11_FLAG_GC_FONT 0x00004000 #define X11_FLAG_GC_EXPOSE 0x00010000 void x11_create_gc(int sock, struct x11_connection *conn, uint32_t id, uint32_t target, uint32_t flags, uint32_t *list) { uint16_t flag_count = count_bits(flags); uint16_t length = 4 + flag_count; uint32_t *packet = sbrk(length*4); packet[0]=X11_OP_REQ_CREATE_GC | length<<16; packet[1]=id; packet[2]=target; packet[3]=flags; int i; for (i=0;i<flag_count;i++) packet[4+i] = list[i]; write(sock,packet,length*4); sbrk(-(length*4)); return; } #define X11_FLAG_WIN_BG_IMG 0x00000001 #define X11_FLAG_WIN_BG_COLOR 0x00000002 #define X11_FLAG_WIN_BORDER_IMG 0x00000004 #define X11_FLAG_WIN_BORDER_COLOR 0x00000008 #define X11_FLAG_WIN_EVENT 0x00000800 void x11_create_win(int sock, struct x11_connection *conn, uint32_t id, uint32_t parent, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t border, uint16_t group, uint32_t visual, uint32_t flags, uint32_t *list) { uint16_t flag_count = count_bits(flags); uint16_t length = 8 + flag_count; uint32_t *packet = sbrk(length*4); packet[0]=X11_OP_REQ_CREATE_WINDOW | length<<16; packet[1]=id; packet[2]=parent; packet[3]=x | y<<16; packet[4]=w | h<<16; packet[5]=border<<16 | group; packet[6]=visual; packet[7]=flags; int i; for (i=0;i<flag_count;i++) packet[8+i] = list[i]; write(sock,packet,length*4); sbrk(-(length*4)); return; } void x11_map_window(int sock, struct x11_connection *conn, uint32_t id) { uint32_t packet[2]; packet[0]=X11_OP_REQ_MAP_WINDOW | 2<<16; packet[1]=id; write(sock,packet,8); return; } #define X11_IMG_FORMAT_MONO 0x0 #define X11_IMG_FORMAT_XY 0x01 #define X11_IMG_FORMAT_Z 0x02 void x11_put_img(int sock, struct x11_connection *conn, uint8_t format, uint32_t target, uint32_t gc, uint16_t w, uint16_t h, uint16_t x, uint16_t y, uint8_t depth, void *data) { uint32_t packet[6]; uint16_t length = ((w * h)) + 6; packet[0]=X11_OP_REQ_PUT_IMG | format<<8 | length<<16; packet[1]=target; packet[2]=gc; packet[3]=w | h<<16; packet[4]=x | y<<16; packet[5]=depth<<8; write(sock,packet,24); write(sock,data,(w*h)*4); return; } int main(int argc, char **argv) { int sockfd, clilen, srv_len, openfd; if (argc==1) { struct sockaddr_un serv_addr={0}; //Create the socket sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd < 0) error("Error opening socket

",21); serv_addr.sun_family = AF_UNIX; strcopy(serv_addr.sun_path, "/tmp/.X11-unix/X0", 0); srv_len = sizeof(struct sockaddr_un); connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)); } else { struct sockaddr_in serv_addr={0}; //Create the socket sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error("Error opening socket

",21); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr=0x0100007f; serv_addr.sin_port = htons(6001); connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)); } struct x11_connection conn = {0}; x11_handshake(sockfd,&conn); int gc = x11_generate_id(&conn); uint32_t val[32]; val[0]=0x00aa00ff; val[1]=0x00000000; x11_create_gc(sockfd,&conn,gc,conn.root[0].id,X11_FLAG_GC_BG|X11_FLAG_GC_EXPOSE,val); int win = x11_generate_id(&conn); val[0]=0x00aa00ff; x11_create_win(sockfd,&conn,win,conn.root[0].id,200,200,400,200,1,1,conn.root[0].root_visual_id,X11_FLAG_WIN_BG_COLOR,val); x11_map_window(sockfd,&conn,win); uint32_t data[1600]={0}; int i; for (i=0;i<1600;i++) data[i]=0x0055ff33; x11_put_img(sockfd,&conn,X11_IMG_FORMAT_Z,win,gc,40,40,0,0,conn.root[0].depth,data); while (1) ; }

Wayland (Coming Soon)

Mir (Coming Soon)

Useful Drawing Concepts

Double Buffering

static uint32_t *back_buffer; void init_back_buffer() { back_buffer = (uint8_t*)mmap(0, vinfo.yres_virtual * finfo.line_length, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, (off_t)0); }

for (x=0;x<vinfo.xres;x++) for (y=0;y<vinfo.yres;y++) { long location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y+vinfo.yoffset) * finfo.line_length; *((uint32_t*)(back_buffer + location)) = pixel_color(0xFF,0x00,0xFF, &vinfo); }

inline void swap_buffers() { int i; for (i=0;i<(vinfo.yres_virtual * finfo.line_length)/4;i++) { ((uint32_t*)(fbp))[i] = back_buffer[i]; } }

uint8_t *fbp, //Front buffer base pointer *bbp; //back buffer base pointer void init_fbdev() { ... fbp = mmap(0, screensize*2, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, (off_t)0); bbp = fbp + screensize; } void clear() { for (x=0;x<vinfo.xres;x++) for (y=0;y<vinfo.yres;y++) { long location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y+vinfo.yoffset) * finfo.line_length; *((uint32_t*)(bbp + location)) = pixel_color(0xFF,0x00,0xFF, &vinfo); } } inline void swap_buffers() { if (vinfo.yoffset==0) vinfo.yoffset = screensize; else vinfo.yoffset=0; //"Pan" to the back buffer ioctl(fb_fd, FBIOPAN_DISPLAY, &vinfo); //Update the pointer to the back buffer so we don't draw on the front buffer long tmp; tmp=fbp; fbp=bbp; bbp=tmp; }

Drawing Primatives

Lines

void draw_horizontal_line(int x1, int x2, int y, uint32_t pixel) { int i; for (i=x1;i<x2;i++) draw(i,y,pixel); } void draw_vertical_line(int x, int y1, int y2, uint32_t pixel) { int i; for (i=y1;i<y2;i++) draw(x,i,pixel); }

void draw_line(int x1, int y1, int x2, int y2, uint32_t pixel) { int i,dx,dy,sdx,sdy,dxabs,dyabs,x,y,px,py; dx=x2-x1; //Delta x dy=y2-y1; //Delta y dxabs=abs(dx); //Absolute delta dyabs=abs(dy); //Absolute delta sdx=(dx>0)?1:-1; //signum function sdy=(dy>0)?1:-1; //signum function x=dyabs>>1; y=dxabs>>1; px=x1; py=y1; if (dxabs>=dyabs) { for(i=0;i<dxabs;i++) { y+=dyabs; if (y>=dxabs) { y-=dxabs; py+=sdy; } px+=sdx; draw(px,py,pixel); } } else { for(i=0;i<dyabs;i++) { x+=dxabs; if (x>=dyabs) { x-=dyabs; px+=sdx; } py+=sdy; draw(px,py,pixel); } } }

Circles

//Draw a circle at (cx,cy) void draw_circle(double cx, double cy, int radius, uint32_t pixel) { inline void plot4points(double cx, double cy, double x, double y, uint32_t pixel) { draw(cx + x, cy + y,pixel); draw(cx - x, cy + y,pixel); draw(cx + x, cy - y,pixel); draw(cx - x, cy - y,pixel); } inline void plot8points(double cx, double cy, double x, double y, uint32_t pixel) { plot4points(cx, cy, x, y,pixel); plot4points(cx, cy, y, x,pixel); } int error = -radius; double x = radius; double y = 0; while (x >= y) { plot8points(cx, cy, x, y, pixel); error += y; y++; error += y; if (error >= 0) { error += -x; x--; error += -x; } } }

inline void plot4points(double cx, double cy, double x, double y, uint32_t pixel) { draw_horizontal_line(cx + x, cx - x, cy + y,pixel); draw_horizontal_line(cx + x, cx - x, cy - y,pixel); }