From 3913d1778318cd0c6bfb871148d38abb33ec7fd3 Mon Sep 17 00:00:00 2001 From: nasr Date: Wed, 28 Jan 2026 13:13:40 +0100 Subject: checkpoint --- .clangd | 70 +++--- base/base.c | 69 ++++++ base/base.h | 85 +++++++ base/base_arena.c | 86 +++++++ base/base_arena.h | 49 ++++ build.sh | 35 +-- library/arena.c | 81 +++++++ library/arena.h | 69 ++++++ library/base.h | 42 ++++ library/clang-format | 104 +++++++++ library/clangd | 59 +++++ main | Bin 72320 -> 85960 bytes main.c | 401 +++++++++++++++++---------------- xlib-tutorial/2nd-program-anatomy.html | 282 +++++++++++++++++++++++ xlib-tutorial/Xlib.pdf | Bin 0 -> 357941 bytes xlib-tutorial/index.html | 116 ++++++++++ xlib-tutorial/prog-1.cc | 29 +++ xlib-tutorial/prog-2.cc | 74 ++++++ xlib-tutorial/server.html | 89 ++++++++ 19 files changed, 1502 insertions(+), 238 deletions(-) create mode 100644 base/base.c create mode 100644 base/base.h create mode 100644 base/base_arena.c create mode 100644 base/base_arena.h create mode 100644 library/arena.c create mode 100644 library/arena.h create mode 100644 library/base.h create mode 100644 library/clang-format create mode 100644 library/clangd create mode 100644 xlib-tutorial/2nd-program-anatomy.html create mode 100644 xlib-tutorial/Xlib.pdf create mode 100644 xlib-tutorial/index.html create mode 100644 xlib-tutorial/prog-1.cc create mode 100644 xlib-tutorial/prog-2.cc create mode 100644 xlib-tutorial/server.html diff --git a/.clangd b/.clangd index 1fa5635..759ad19 100644 --- a/.clangd +++ b/.clangd @@ -1,39 +1,39 @@ CompileFlags: - Add: - - -std=c99 - - -xc - - - -Iinclude - - -Wall - - -Wextra - - -Wpedantic - - -Wshadow - - -Wconversion - - -Wsign-conversion - - -Wmissing-declarations - - -Wundef - - -Wpointer-arith - - -Wcast-align - - -Wcast-qual - - -Wwrite-strings - - -Wswitch-enum - - -Wformat=2 - - -Wstrict-aliasing=2 - - -Werror=implicit-function-declaration - - -Werror=implicit-int - - -Werror=incompatible-pointer-types - - -Werror=return-type - - -Wformat-security - - -Wnull-dereference - - -Wmisleading-indentation - - - -Wuninitialized - - -Werror - - -Wdouble-promotion - - -Wstrict-overflow=2 - - - -D_POSIX_C_SOURCE=200809L - - "-I/include" + # Add: + # - -std=c99 + # - -xc + # + # - -Iinclude + # - -Wall + # - -Wextra + # - -Wpedantic + # - -Wshadow + # - -Wconversion + # - -Wsign-conversion + # - -Wmissing-declarations + # - -Wundef + # - -Wpointer-arith + # - -Wcast-align + # - -Wcast-qual + # - -Wwrite-strings + # - -Wswitch-enum + # - -Wformat=2 + # - -Wstrict-aliasing=2 + # - -Werror=implicit-function-declaration + # - -Werror=implicit-int + # - -Werror=incompatible-pointer-types + # - -Werror=return-type + # - -Wformat-security + # - -Wnull-dereference + # - -Wmisleading-indentation + # + # - -Wuninitialized + # - -Werror + # - -Wdouble-promotion + # - -Wstrict-overflow=2 + # + # - -D_POSIX_C_SOURCE=200809L + # - "-I/include" Remove: - -std=* diff --git a/base/base.c b/base/base.c new file mode 100644 index 0000000..ba8ba32 --- /dev/null +++ b/base/base.c @@ -0,0 +1,69 @@ +#include "base/base.h" +#include "stddef.h" + +/** + * Helper function to parse strings to int using ascii codes + * */ +local_internal u64 +parse_u64(char *buf, size_t len) +{ + u64 value = 0; + + for ( + size_t buffer_idx = 0; + buffer_idx < len; + ++buffer_idx) + { + char c = buf[buffer_idx]; + if (c < '0' || c > '9') + { + break; + } + value = value * 10 + (c - '0'); + } + + return value; +} + +/* + * is_numeric - Check if a string contains only digits + * @s: String to check + * + * Return: 1 if string contains only numeric characters, 0 otherwise + */ +local_internal b8 +is_numeric(char *s) +{ + for (; *s; ++s) + { + if (*s < '0' || *s > '9') + { + return 0; + } + } + return 1; +} + +local_internal b8 +compare_string(const char *c1, const char *c2) +{ + if (sizeof(c1) != sizeof(c2)) + { + return -1; + } + + for ( + u64 word_idx = 0; + word_idx <= sizeof(*c1); + ++word_idx) + { + if (*c1 != *c2) + { + return -1; + } + ++c1; + ++c2; + } + + return 0; +} diff --git a/base/base.h b/base/base.h new file mode 100644 index 0000000..8ce6cba --- /dev/null +++ b/base/base.h @@ -0,0 +1,85 @@ +#ifndef BASE_H +#define BASE_H + +#include +#include + +/* assert an expression and output the file and the line */ + +#define RED "\x1b[31m" +#define GREEN "\x1b[32m" +#define RESET "\x1b[0m" + +#define test_assert(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf(stderr, RED " [FAILED] %s:%d: expr:%s test:%s\n" RESET, __FILE__, __LINE__, #expr, __func__); \ + abort(); \ + } \ + else\ + { \ + fprintf(stdout, GREEN "[PASSED] %s\n" RESET, __func__); \ + }\ + } while (0) + +#define global_variable static +#define local_persist static +#define local_internal static + +#define ERR_OK 0 +#define ERR_IO 1 +#define ERR_PARSE 2 +#define ERR_PERM 3 +#define ERR_INVALID 4 + +#define KiB(n) (((u64)(n)) << 10) +#define MiB(n) (((u64)(n)) << 20) +#define GiB(n) (((u64)(n)) << 30) + +#define BUFFER_SIZE_SMALL 128 +#define BUFFER_SIZE_DEFAULT 256 +#define BUFFER_SIZE_LARGE 512 +#define PATH_MAX_LEN 4096 + +#define DEPRECATED __attribute__((__deprecated__)) + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +typedef float f32; +typedef double f64; + +typedef i32 b32; +typedef i16 b16; +typedef u8 b8; + +typedef uintptr_t umm; +typedef intptr_t smm; + +#define TRUE 1 +#define FALSE 0 + +local_internal u64 +parse_u64(char *buf, size_t len); + +local_internal b8 +is_numeric(char *s); + +local_internal b8 +compare_string(const char *c1, const char *c2); + +/* + * TODO(nasr): macro for verbose assertions + * + * */ + +#endif diff --git a/base/base_arena.c b/base/base_arena.c new file mode 100644 index 0000000..16997b2 --- /dev/null +++ b/base/base_arena.c @@ -0,0 +1,86 @@ +/** + * TODO(nasr): remove stdlib + * */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base_arena.h" + +/* TODO(nasr): reserve pages support */ +/* TODO(nasr): check if an arena has been used before */ + +local_internal mem_arena * +arena_create(u64 capacity) +{ + mem_arena *arena = (mem_arena *)mmap(0, capacity, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (arena == MAP_FAILED) + { + return NULL; + } + + arena->capacity = capacity; + arena->pos = ARENA_BASE_POS; + + return arena; +} + +/* + * make it a void pointer to allow implicit conversion + * */ +local_internal void +arena_destroy(mem_arena *arena) +{ + munmap(arena, arena->capacity); +} + +local_internal void * +arena_push(mem_arena *arena, u64 size, b32 non_zero) +{ + u64 pos_aligned = ALIGN_UP_POW2(arena->pos, ARENA_ALIGN); + u64 new_pos = pos_aligned + size; + + if (new_pos > arena->capacity) + { + assert(0); + return NULL; + } + + arena->pos = new_pos; + /* + * cast to u8 to be able to do pointer arithemtic + * */ + u8 *out = (u8 *)arena + pos_aligned; + + if (!non_zero) + { + memset(out, 0, size); + } + return out; +} + +local_internal void +arena_pop(mem_arena *arena, u64 size) +{ + size = MIN(size, arena->pos - ARENA_BASE_POS); + arena->pos -= size; +} + +local_internal void +arena_pop_to(mem_arena *arena, u64 pos) +{ + u64 size = pos < arena->pos ? arena->pos - pos : 0; + arena_pop(arena, size); +} + +local_internal void +arena_clear(mem_arena *arena) +{ + arena_pop_to(arena, ARENA_BASE_POS); +} diff --git a/base/base_arena.h b/base/base_arena.h new file mode 100644 index 0000000..19c4f3d --- /dev/null +++ b/base/base_arena.h @@ -0,0 +1,49 @@ +#ifndef ARENA_H +#define ARENA_H + +#include "base.h" + +/** + * Arena Helper macro's + * */ + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define ALIGN_UP_POW2(n, p) (((u64)(n) + ((u64)(p) - 1)) & (~((u64)(p) - 1))) + +#define PUSH_STRUCT(arena, T) (T *)arena_push((arena), sizeof(T), 0) +#define PUSH_STRUCT_NZ(arena, T) (T *)arena_push((arena), sizeof(T), 1) +#define PUSH_ARRAY(arena, T, n) (T *)arena_push((arena), sizeof(T) * (n), 0) +#define PUSH_ARRAY_NZ(arena, T, n) (T *)arena_push((arena), sizeof(T) * (n), 1) + +#define ARENA_BASE_POS (sizeof(mem_arena)) +#define ARENA_ALIGN (sizeof(void *)) + +typedef struct mem_arena mem_arena; + +struct mem_arena +{ + u64 capacity; + u64 pos; +}; + +local_internal mem_arena * +arena_create(u64 capacity); + +local_internal void +arena_destroy(mem_arena *arena); + +local_internal void * +arena_push(mem_arena *arena, u64 size, b32 non_zero); + +local_internal void +arena_pop(mem_arena *arena, u64 size); + +local_internal void +arena_pop_to(mem_arena *arena, u64 pos); + +local_internal void +arena_clear(mem_arena *arena); + + +#endif diff --git a/build.sh b/build.sh index 578bc8b..80f3d5a 100755 --- a/build.sh +++ b/build.sh @@ -1,24 +1,31 @@ #!/bin/sh set -eu +# Toolchain CC=clang + +# Files SRC=main.c OUT=main +# Include paths +INCLUDES="-I." + +# Compiler flags +CFLAGS=" +-g +-O0 +" -$CC \ - -Wall -Wextra \ - -v \ - "$SRC" \ - -o "$OUT" \ - -lX11 \ - -lm -echo +# Linker flags +LDFLAGS=" +-lX11 +-I. +-lm +" -echo "== Binary info ==" -file "$OUT" -ldd "$OUT" -echo +echo "==> Building $OUT" +$CC $CFLAGS $INCLUDES "$SRC" -o "$OUT" $LDFLAGS -echo "== Running ==" -LD_DEBUG=libs ./"$OUT" +echo "==> Running $OUT" +./"$OUT" diff --git a/library/arena.c b/library/arena.c new file mode 100644 index 0000000..39711d5 --- /dev/null +++ b/library/arena.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arena.h" + +mem_arena * +arena_create(u64 capacity) +{ + mem_arena *arena = mmap(0, capacity, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (arena == MAP_FAILED) + { + assert(0); + } + + arena->capacity = capacity; + arena->pos = ARENA_BASE_POS; + + return arena; +} + +// make it a void pointer to allow implicit conversion +void +arena_destroy(mem_arena *arena) +{ + munmap(arena, arena->capacity); +} + +void * +arena_push(mem_arena *arena, u64 size, b32 non_zero) +{ + u64 pos_aligned = ALIGN_UP_POW2(arena->pos, ARENA_ALIGN); + u64 new_pos = pos_aligned + size; + + if (new_pos > arena->capacity) + { + assert(0); + return NULL; + } + + arena->pos = new_pos; + // cast to u8 to be able to do pointer arithemtic + u8 *out = (u8 *)arena + pos_aligned; + + if (!non_zero) + { + memset(out, 0, size); + } + return out; +} +void +arena_pop(mem_arena *arena, u64 size) +{ + size = MIN(size, arena->pos - ARENA_BASE_POS); + arena->pos -= size; +} + +void +arena_pop_to(mem_arena *arena, u64 pos) +{ + u64 size = pos < arena->pos ? arena->pos - pos : 0; + arena_pop(arena, size); +} + +void +arena_clear(mem_arena *arena) +{ + arena_pop_to(arena, ARENA_BASE_POS); +} + +#define PUSH_STRUCT(arena, T) (T *)arena_push((arena), sizeof(T), 0) +#define PUSH_STRUCT_NZ(arena, T) (T *)arena_push((arena), sizeof(T), 1) +#define PUSH_ARRAY(arena, T, n) (T *)arena_push((arena), sizeof(T) * (n), 0) +#define PUSH_ARRAY_NZ(arena, T, n) (T *)arena_push((arena), sizeof(T) * (n), 1) + diff --git a/library/arena.h b/library/arena.h new file mode 100644 index 0000000..330023e --- /dev/null +++ b/library/arena.h @@ -0,0 +1,69 @@ +#ifndef ARENA_H +#define ARENA_H + +#include "base.h" + +/** + * Arena Helper macro's + * */ + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define ALIGN_UP_POW2(n, p) (((u64)(n) + ((u64)(p) - 1)) & (~((u64)(p) - 1))) + + + +/* + * Represents a disk partition with major/minor device numbers and block count. + */ + +/** + * replacing malloc/free with arena allocaters + * + * */ + +#define ARENA_BASE_POS (sizeof(mem_arena)) +// void * for the size of a pointer on the machine, 64/32bit comp +#define ARENA_ALIGN (sizeof(void *)) + + +static inline u64 KiB(u64 n) { return n << 10; } +static inline u64 MiB(u64 n) { return n << 20; } +static inline u64 GiB(u64 n) { return n << 30; } + +typedef struct mem_arena mem_arena; + + +struct mem_arena +{ + u64 capacity; + u64 pos; +} ; + +// arena prototypes +mem_arena * +arena_create(u64 capacity); +// make it a void pointer to allow implicit conversion +void +arena_destroy(mem_arena *arena); + +void * +arena_push(mem_arena *arena, u64 size, b32 non_zero); + +void +arena_pop(mem_arena *arena, u64 size); + +void +arena_pop_to(mem_arena *arena, u64 pos); + +void +arena_clear(mem_arena *arena); + + +#define PUSH_STRUCT(arena, T) (T *)arena_push((arena), sizeof(T), 0) +#define PUSH_STRUCT_NZ(arena, T) (T *)arena_push((arena), sizeof(T), 1) +#define PUSH_ARRAY(arena, T, n) (T *)arena_push((arena), sizeof(T) * (n), 0) +#define PUSH_ARRAY_NZ(arena, T, n) (T *)arena_push((arena), sizeof(T) * (n), 1) + + +#endif diff --git a/library/base.h b/library/base.h new file mode 100644 index 0000000..74b6303 --- /dev/null +++ b/library/base.h @@ -0,0 +1,42 @@ +#ifndef BASE_H +#define BASE_H + +#include +#include + +#define OK 0 +#define ERR_IO 1 +#define ERR_PARSE 2 +#define ERR_PERM 3 +#define ERR_INVALID 4 + +enum { + BUFFER_SIZE_SMALL = 128, + BUFFER_SIZE_DEFAULT = 256, + BUFFER_SIZE_LARGE = 512, + PATH_MAX_LEN = 4096 +}; + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +typedef float f32; +typedef double f64; + +typedef i32 b32; +typedef i16 b16; +typedef u8 b8; + +#define TRUE 1 +#define FALSE 0 + + + +#endif diff --git a/library/clang-format b/library/clang-format new file mode 100644 index 0000000..3c61d5e --- /dev/null +++ b/library/clang-format @@ -0,0 +1,104 @@ +BasedOnStyle: LLVM +Language: C + +# ------------------------------------------------------------------- +# Indentation & layout +# ------------------------------------------------------------------- +IndentWidth: 2 +TabWidth: 2 +UseTab: Never +ContinuationIndentWidth: 2 + +IndentCaseLabels: true +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: NoIndent + +# ------------------------------------------------------------------- +# Line breaking +# ------------------------------------------------------------------- +ColumnLimit: 0 + +AllowAllParametersOfDeclarationOnNextLine: false +AllowAllArgumentsOnNextLine: false + +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false + +AlwaysBreakAfterReturnType: All +AlwaysBreakTemplateDeclarations: No # harmless for C + +BreakBeforeBinaryOperators: None + +# ------------------------------------------------------------------- +# Braces (Allman style) +# ------------------------------------------------------------------- +BreakBeforeBraces: Allman + +BraceWrapping: + AfterCaseLabel: true + BeforeElse: true + BeforeCatch: true + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true + +# NOTE: +# AfterControlStatement / AfterFunction / AfterStruct / AfterEnum / AfterUnion +# are IMPLIED by Allman and must NOT be redundantly specified. + +# ------------------------------------------------------------------- +# Spacing +# ------------------------------------------------------------------- +SpaceBeforeParens: ControlStatements +SpaceBeforeAssignmentOperators: true +SpaceBeforeRangeBasedForLoopColon: true # ignored in C, harmless + +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpacesInAngles: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 + +PointerAlignment: Right +DerivePointerAlignment: false + +# ------------------------------------------------------------------- +# Alignment (explicitly disabled) +# ------------------------------------------------------------------- +AlignAfterOpenBracket: DontAlign +AlignOperands: false +AlignTrailingComments: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: DontAlign + +# ------------------------------------------------------------------- +# Comments +# ------------------------------------------------------------------- +ReflowComments: false +CommentPragmas: '^ dont touch:' +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 + +# ------------------------------------------------------------------- +# Includes +# ------------------------------------------------------------------- +SortIncludes: Never +IncludeBlocks: Preserve + +# ------------------------------------------------------------------- +# Macros & preprocessor +# ------------------------------------------------------------------- +MacroBlockBegin: '' +MacroBlockEnd: '' +SpaceAfterCStyleCast: false + +# ------------------------------------------------------------------- +# C-specific +# ------------------------------------------------------------------- +Cpp11BracedListStyle: false +DisableFormat: false diff --git a/library/clangd b/library/clangd new file mode 100644 index 0000000..624678a --- /dev/null +++ b/library/clangd @@ -0,0 +1,59 @@ +CompileFlags: + Add: + - -std=c99 + - -xc + + - -Iinclude + + - -Wall + - -Wextra + - -Wpedantic + - -Wshadow + - -Wconversion + - -Wsign-conversion + - -Wmissing-declarations + - -Wundef + - -Wpointer-arith + - -Wcast-align + - -Wcast-qual + - -Wwrite-strings + - -Wswitch-enum + - -Wformat=2 + - -Wstrict-aliasing=2 + - -Werror=implicit-function-declaration + - -Werror=implicit-int + - -Werror=incompatible-pointer-types + - -Werror=return-type + - -Wformat-security + - -Wnull-dereference + - -Wmisleading-indentation + + - -Wunused + - -Wuninitialized + - -Werror + - -Wdouble-promotion + - -Wstrict-overflow=2 + + - -D_POSIX_C_SOURCE=200809L + - "-I/include" + + Remove: + - -std=* + - -O* + - -march=* + - -mtune=* + + +Hover: + ShowAKA: true + +InlayHints: + Enabled: true + ParameterNames: true + DeducedTypes: true + +Completion: + AllScopes: true + +Index: + Background: Build diff --git a/main b/main index 529c2d0..28736dd 100755 Binary files a/main and b/main differ diff --git a/main.c b/main.c index 0118363..4c43245 100644 --- a/main.c +++ b/main.c @@ -4,231 +4,254 @@ #include #include #include +#include -#define NIL (0) +#include "base/base.h" +#include "base/base_arena.h" -#define internal static -#define global static -#define local static +#include "base/base.c" +#include "base/base_arena.c" -typedef uint64_t u64; -typedef uint32_t u32; -typedef uint16_t u16; -typedef uint8_t u8; -typedef int8_t i8; -typedef int16_t i16; -typedef int32_t i32; -typedef int64_t i64; - -typedef float f32; -typedef double f64; - -typedef i16 b16; -typedef i32 b32; +#define NIL 0 typedef struct { - i32 x; - i32 y; - u32 height; - u32 width; - u32 border_width; - i32 window_depth; - u32 window_class; - u64 value_mask; +i32 x; +i32 y; +u32 height; +u32 width; +u32 border_width; +i32 window_depth; +u32 window_class; +u64 value_mask; } WindowProperties; typedef struct { - i32 x; - i32 y; - i32 z; +i32 x; +i32 y; +i32 z; } vertex; void sleep_ms(long ms) { - struct timespec ts; - ts.tv_sec = ms / 1000; - ts.tv_nsec = (ms % 1000) * 1000000L; +struct timespec ts; +ts.tv_sec = ms / 1000; +ts.tv_nsec = (ms % 1000) * 1000000L; - while (nanosleep(&ts, &ts)) - { - NULL; - } +while (nanosleep(&ts, &ts)) +{ +NULL; +} } void move_down(double *y) { - ++*y; +++*y; } void move_up(double *y) { - --*y; +--*y; } +void +move_left(double *x) +{ +--*x; +} + +void +move_right(double *x) +{ +++*x; +} + + + typedef struct { - void (*move)(double *a); +void (*move)(double *a); } movement; +void +handle_destroy(Display *display, GC *gc) +{ +XFreeGC(display, *gc); +XCloseDisplay(display); +} + +typedef struct +{ +i32 x; +i32 y; + +} display_pos; + +typedef struct +{ +i32 x; +i32 y; +i32 z; + +} pos; + + int main() { - Display *MainDisplay = XOpenDisplay(NIL); - - Window root = XDefaultRootWindow(MainDisplay); - int screen = DefaultScreen(MainDisplay); - - Visual *v = DefaultVisual(MainDisplay, screen); - - XSetWindowAttributes wa = { - .background_pixmap = None, - .background_pixel = BlackPixel(MainDisplay, DefaultScreen(MainDisplay)), - .border_pixmap = CopyFromParent, - .border_pixel = 0, - .bit_gravity = ForgetGravity, - .win_gravity = NorthWestGravity, - .backing_store = NotUseful, - .backing_planes = 1, - .backing_pixel = 0, - .save_under = False, - .event_mask = {}, - .do_not_propagate_mask = {}, - .override_redirect = False, - .colormap = CopyFromParent, - .cursor = None - }; - - i32 dp_heigth = DisplayHeight(MainDisplay, screen); - i32 dp_width = DisplayWidth(MainDisplay, screen); - - WindowProperties p = { - - .x = dp_width / 2, - .y = dp_heigth / 2, - .width = (u32)400, - .height = (u32)400, - .border_width = 0, - .window_depth = CopyFromParent, - .window_class = CopyFromParent, - .value_mask = CWBackPixel, - - }; - - Window window = - XCreateWindow( - MainDisplay, - root, - p.x, - p.y, - p.width, - p.height, - p.border_width, - p.window_depth, - p.window_class, - v, - p.value_mask, - &wa); - - XSetWindowBorder(MainDisplay, window, 60); - XSelectInput(MainDisplay, window, ExposureMask | StructureNotifyMask); - XMapWindow(MainDisplay, window); - - double x = p.width / 2; - double y = p.height / 2; - u32 rect_width = 50; - u32 rect_height = 50; - - b16 running = 1; - - u64 color = 0x0000ff00; - - GC gc = XCreateGC(MainDisplay, window, 0, NIL); - GC textGc = XCreateGC(MainDisplay, window, 0, NIL); - XSetForeground(MainDisplay, gc, color); - - double *pX = &x; - double *pY = &y; - - movement m = { - .move = move_down - }; - - while (running) - { - if (*pY + rect_height >= p.height) - { - m.move = move_up; - } - else if (*pY <= 0) - { - m.move = move_down; - } - - char words[] = "working"; - - XTextItem ti = { - .chars = words, - .nchars = (int)strlen(words), - .delta = 0, - .font = None - }; - - XClearWindow(MainDisplay, window); - - XDrawText( - MainDisplay, - window, - textGc, - 50, - 50, - &ti, - 1); - - XFillRectangle( - MainDisplay, - window, - gc, - (i32)*pX, - (i32)*pY, - rect_height, - rect_width); - - XFillRectangle( - MainDisplay, - window, - gc, - (i32)*pX + 100, - (i32)*pY, - rect_height, - rect_width); - - XFillRectangle( - MainDisplay, - window, - gc, - (i32)*pX - 100, - (i32)*pY, - rect_height, - rect_width); - - m.move(pY); - - sleep_ms(10); - - XFlush(MainDisplay); - } - - XFreeGC(MainDisplay, gc); - XFreeGC(MainDisplay, textGc); - XCloseDisplay(MainDisplay); - - return 0; +Display *MainDisplay = XOpenDisplay(0); +mem_arena *arena = arena_create(MiB(8)); + +Window root = XDefaultRootWindow(MainDisplay); +int screen = DefaultScreen(MainDisplay); + +Visual *v = DefaultVisual(MainDisplay, screen); + +XSetWindowAttributes wa = { +.background_pixmap = None, +.background_pixel = BlackPixel(MainDisplay, DefaultScreen(MainDisplay)), +.border_pixmap = CopyFromParent, +.border_pixel = 0, +.bit_gravity = ForgetGravity, +.win_gravity = NorthWestGravity, +.backing_store = NotUseful, +.backing_planes = 1, +.backing_pixel = 0, +.save_under = False, +.event_mask = {}, +.do_not_propagate_mask = {}, +.override_redirect = False, +.colormap = CopyFromParent, +.cursor = None +}; + +i32 dp_heigth = DisplayHeight(MainDisplay, screen); +i32 dp_width = DisplayWidth(MainDisplay, screen); + +WindowProperties p = { + +.x = dp_width / 2, +.y = dp_heigth / 2, +.height = (u32)800, +.width = (u32)1200, +.border_width = 0, +.window_depth = CopyFromParent, +.window_class = CopyFromParent, +.value_mask = CWBackPixel, + +}; + + +Window window = +XCreateWindow( +MainDisplay, +root, +p.x, +p.y, +p.width, +p.height, +p.border_width, +p.window_depth, +p.window_class, +v, +p.value_mask, +&wa); + +Pixmap pixmap = XCreatePixmap(MainDisplay, window, dp_width, dp_heigth, 1); + +XSetWindowBorder(MainDisplay, window, 60); +XSelectInput(MainDisplay, window, ExposureMask | StructureNotifyMask); +XMapWindow(MainDisplay, window); + + +double x = p.width / 2; +double y = p.height / 2; + +u32 rect_width = 50; +u32 rect_height = 50; + +u64 color = 0x0000ff00; + +GC gc = XCreateGC(MainDisplay, window, 0, NIL); +XSetForeground(MainDisplay, gc, color); + + +double *pX = &x; +double *pY = &y; + +movement m = { +.move = move_down +}; + +XEvent event; +XNextEvent(MainDisplay, &event); + +for (;;) +{ +switch (event.type) +case KeyPress: +case KeyRelease: +{ + + +} +default: +{ +if (*pX + rect_width >= p.width) +{ +m.move = move_left; +} +else if (*pX <= 0) +{ +m.move = move_right; +} + +char words[] = "working"; + +XTextItem ti = { +.chars = words, +.nchars = (int)strlen(words), +.delta = 0, +.font = None +}; + +XClearWindow(MainDisplay, window); + +pos *p = arena_push(arena, sizeof(*p), 0); + +p->z = 10; +p->x = ((i32)*pX * 10) / p->z; +p->y = ((i32)*pY * 10) / p->z; + +XFillRectangle( +MainDisplay, +window, +gc, +(i32)p->x, +(i32)p->y, +rect_height, +rect_width); + +m.move(&x); + +sleep_ms(1); + +XFlush(MainDisplay); +} + +case DestroyNotify: +{ +// handle_destroy(MainDisplay, &gc); +} +} + +arena_clear(arena); +return 0; } diff --git a/xlib-tutorial/2nd-program-anatomy.html b/xlib-tutorial/2nd-program-anatomy.html new file mode 100644 index 0000000..d156f7a --- /dev/null +++ b/xlib-tutorial/2nd-program-anatomy.html @@ -0,0 +1,282 @@ + + +Xlib programming tutorial: anatomy of the most basic Xlib program + + + +

Anatomy of the most basic Xlib program

+ +The program starts with the legal stuff: + +

+#include <X11/Xlib.h> // Every Xlib program must include this
+#include <assert.h>   // I include this to test return values the lazy way
+#include <unistd.h>   // So we got the profile for 10 seconds
+
+#define NIL (0)       // A name for the void pointer
+
+ +Then the serious thing. First we open a connection to the server. + +

+Display *dpy = XOpenDisplay(NIL);
+assert(dpy);
+
+ +If it fails (and it may), XOpenDisplay() will return NIL. + +

+ +We gonna create a window, but we need to get the window's background +color first. X uses a quite complex color model in order to accommodate +to every conceivable piece of hardware. Each color is encoded by an integer, +but the integer for a given color may change from a machine to another +one, and even on the same machine, from an execution of the program to +the next. The only "colors" that X guarantees to exist are black and +white. We can get them using the +BlackPixel() +and +WhitePixel() +macros. + +


+      int blackColor = BlackPixel(dpy, DefaultScreen(dpy));
+      int whiteColor = WhitePixel(dpy, DefaultScreen(dpy));
+
+ +As we yet can see, most of the Xlib calls take the "display" (the +value returned by +XOpenDisplay()) +as their first parameter. You want to know why ? + +

+ +There is still someting magic, (the +DefaultScreen() +stuff), but we gonna keep it for a later +explanation. We now can +create our window: + +


+      // Create the window
+
+      Window w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 
+				     200, 100, 0, blackColor, blackColor);
+
+ +Unlike what appears in the dialog, we use the +function +XCreateSimpleWindow() +instead of +XCreateWindow(). +XCreateSimpleWindow() +is not really simpler than +XCreateWindow() +(it takes only a few less parameters), but it uses less concepts, so +we gonna stick to it for now. There are still a bunch of parameters to +explain: + +
    + +
  • dpy is the usual display connection (remember). + +

  • DefaultRootWindow(dpy): yet another parameter that may +seem magic until now. This is the "parent window" of the window we are +creating. The window we create appears inside its parent, and is +bounded by it (the window is said to be "clipped" by its +parent). Those who guess from the name "Default" that they may be +other root windows guess right. More on this later. For now, the default +root window makes appear our window on the screen, and will give the +window manager a chance to decorate +our window. + +

  • 0, 0 These are the coordinates of the upper left +corner of the window (the origin of the coordinates in X is at the +upper left, contrary to most mathematical textbooks). The dimensions, +like every dimensions in X, are in pixels (X does not support +user-defined scales, unlike some other graphical systems like +OpenGL). + +

    + +Contrary to what may seem, there is very little chance for the window +to appear, at 0,0. The reason is that the +window manager will put the window +at its policy-defined position. + +

  • 200, 100: these are the width and height of the +window, in pixels. + +

  • 0: this is the width of the window's border. This +has nothing to do with the border appended by the +window manager, so this is most +often set to zero. + +

  • blackColor, blackColor: these are the colors of the +window's border (NOT the +window manager's border), and the +window's background, respectively. +XCreateSimpleWindow() +clears the window when created, +XCreateWindow() +does not. + +
+ +

+      // We want to get MapNotify events
+
+      XSelectInput(dpy, w, StructureNotifyMask);
+
+ +As we're starting to know, X is based upon a +client-server architecture. The X server +sends events to the client (the program we're writing), to keep it +informed of the modifications in the server. There are many of them +(each time a window is created, moved, masked, unmasked, and many +other things), so a client must tell the server the events it is +interested in. With this XSelectInput() stuff, we tell the +server we want to be informed of "structural" changes occuring on the +w window. Creation and mapping are such changes. There is no +way to be informed for example of only mapping modification, and not +creations, so we've to take everything. In this particular application +we're interesting in "mapping" events (grosso modo, the window +appears on the screen). + +

+      // "Map" the window (that is, make it appear on the screen)
+
+      XMapWindow(dpy, w);
+
+ +And (once again) this is a client-server +system. The map request is asynchronous, meaning that the time this +instruction is executed doesn't tell us when the window is actually +mapped. To be sure, we've to wait for the server to send us a +MapNotify +event (this is why we want to be sensitive to such events). + +

+      // Create a "Graphics Context"
+
+      GC gc = XCreateGC(dpy, w, 0, NIL);
+
+ +Yet another magic stuff. But mastering them is the reason of the +existence of this tutorial... + +

+ +For several reasons, the graphical model of X is stateless, meaning +that the server doesn't remember (among other things) attributes such +as the drawing color, the thickness of the lines and so on. Thus, +we've to give all these parameters to the server on each +drawing request. To avoid passing two dozens of parameters, many of +them unchanged from one request to the next, X uses an object called +the Graphics Context, or GC for short. We store in the +graphics context all the needed parameters. Here, we want the color +used to draw lines, called the foregound color: + +


+      // Tell the GC we draw using the white color
+
+      XSetForeground(dpy, gc, whiteColor);
+
+ +There are many other parameters used to draw a line, but all of them +have reasonable default values. + +

+ +That's okay for now. Everything is set up, and we wait for the window +mapping. + +


+      // Wait for the MapNotify event
+
+      for(;;) {
+	    XEvent e;
+	    XNextEvent(dpy, &e);
+	    if (e.type == MapNotify)
+		  break;
+      }
+
+ +We loop, taking events as they come and discarding them. When we get a +MapNotify, we exit the loop. We may get events other than +MapNotify for two reasons: + +
    +
  • We have selected StructureNotifyMask to get +MapNotify events, but we could get other events as well (such +as ConfigureNotify, telling the window has changed in position, and/or +size). +
  • Some events can be received, even if we don't have asked for +them, they are called "non-maskable". GraphicsExpose is such an +event. +
+ +The non-maskable events are sent only in response to some program +requests (such as copying an area), so they aren't likely to happen in +our context. + +

+ +The +XNextEvent() +procedure is blocking, so if there are no event to read, the program +just wait inside the +XNextEvent(). + +

+ +When we have exited the loop, we have good confidence that the window +appears on the screen. Actually, this may not be the case since, for +example, the user may have iconified it using the +window manager, but for now, we assume the window +actually appears. We can draw our line: + +


+      // Draw the line
+      
+      XDrawLine(dpy, w, gc, 10, 60, 180, 20);
+
+ +The line is between points (10, 60) and (180, 20). The (0,0) is at the +upper left corner of the window, as usual. If the program just +sleeps here, nothing will happen, because, in case you don't +know, X has a client-server +architecture. Thus the request stays in the client, unless we tell it +to go to the server. This is done by XFlush(): + +

+      // Send the "DrawLine" request to the server
+
+      XFlush(dpy);
+
+ +Clever readers may have noticed that we didn't use +XFlush() +before, and it didn't prevent all the requests such as +XMapWindow() +to be sent to the server. The answer is that +XNextEvent() +performs an implicit +XFlush() +before trying to read some events. We have our line now, we just wait +for 10 seconds, so we can make people see how beautiful is our work: + +

+      // Wait for 10 seconds
+
+      sleep(10);
+
+ +That's all for now. In next +lesson, we will have a (very) little more interaction. [to be continued] + +
Christophe Tronche, ch.tronche@computer.org
+ + diff --git a/xlib-tutorial/Xlib.pdf b/xlib-tutorial/Xlib.pdf new file mode 100644 index 0000000..cef1647 Binary files /dev/null and b/xlib-tutorial/Xlib.pdf differ diff --git a/xlib-tutorial/index.html b/xlib-tutorial/index.html new file mode 100644 index 0000000..5e57eaf --- /dev/null +++ b/xlib-tutorial/index.html @@ -0,0 +1,116 @@ + + +Xlib programming: a short tutorial + + + + +

Xlib programming: a short tutorial

+ +I haven't found anything very satisfying on the Web as an Xlib +tutorial. Many of them are too much Motif-oriented for my +taste. Furthermore, I answer questions about X programming almost +daily, so I've started to put together some small coursewares. + +

Important note:

the example programs are written in C++, but +this is mainly for the ability to declare variables anywhere. + +
+ +Let's begin with a short story: the eternal story of the newbie at +Xlib writing his/her first program. + +

+ + +"Ok, I've to open a connection to the X server (whatever this means), +with XOpenDisplay, then create a window with XCreateWindow, then draw +a line with XDrawLine. Then, the program sleeps for ten seconds so I +can see the result. Sounds easy." + + +

+ +The poor newbie writes the program. And nothing happens. He then +calls his wizard friend. + +

+ + +-"Did you perform an XFlush after you've done everything ?
+- No, why ?
+- The requests stay in the client,"
wizard doubletalk, thinks the +poor newbie, " if you +don't." + + +

+ +The poor newbie changes the program. And nothing happens. He then +calls his wizard friend. + +

+ + +-"Did you map your window ?
+- Did I do what ???
+- Creating a window doesn't make it appear on the screen. You've to + map it with XMapWindow first. +
+ +

+ +The poor newbie changes the program. The window appears with nothing +in it (like this). The poor newbie then calls +his wizard friend. + +

+ + +-"Did you wait for a MapNotify before drawing your line ?" (more wizard doubletalk)
+- "No, why ?
+- X has a stateless drawing model, the content of the window may be lost + when the window isn't on the screen."
(overflow, why can't these + wizard guys speak just like you and me ?) "You've to wait for a MapNotify + before drawing." + + +

+ +The poor newbie changes the program. Things are getting more and more +complex. Not as easy as it first seemed. A loop gets the events until a +MapNotify. The window appears with nothing +in it. The poor newbie then calls his wizard friend. + +

+ + +-"I got it, did you select the StructureNotifyMask on your window ?
+- ???
+- Just do it, and everything'll be fine. +
+ +

+ +The poor newbie fixes the program. And the miracle happens ! A line in the +window. Until now, the program looks like this +(it is actually slighty more complex than the dialog may let you think). + +

+ +Now you've learned at least 2 things: + +

    +
  • How to draw a line in a window with X. +
  • Why some may need an X tutorial. +
+ +Now, if you want to learn more and get a deeper understanding of the +program, go to next lesson. + +

More about X.

+ + +
Christophe Tronche, ch.tronche@computer.org
+ + diff --git a/xlib-tutorial/prog-1.cc b/xlib-tutorial/prog-1.cc new file mode 100644 index 0000000..3ba596b --- /dev/null +++ b/xlib-tutorial/prog-1.cc @@ -0,0 +1,29 @@ +// Written by Ch. Tronche (http://tronche.lri.fr:8000/) +// Copyright by the author. This is unmaintained, no-warranty free software. +// Please use freely. It is appreciated (but by no means mandatory) to +// acknowledge the author's contribution. Thank you. +// Started on Thu Jun 26 23:29:03 1997 + +// +// Xlib tutorial: 1st program +// Make a window appear on the screen. +// + +#include // Every Xlib program must include this +#include // I include this to test return values the lazy way +#include // So we got the profile for 10 seconds + +#define NIL (0) // A name for the void pointer + +main() +{ + Display *dpy = XOpenDisplay(NIL); + assert(dpy); + Window w = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, + 200, 100, 0, + CopyFromParent, CopyFromParent, CopyFromParent, + NIL, 0); + XMapWindow(dpy, w); + XFlush(dpy); + sleep(10); +} diff --git a/xlib-tutorial/prog-2.cc b/xlib-tutorial/prog-2.cc new file mode 100644 index 0000000..51ea0e9 --- /dev/null +++ b/xlib-tutorial/prog-2.cc @@ -0,0 +1,74 @@ +// Written by Ch. Tronche (http://tronche.lri.fr:8000/) +// Copyright by the author. This is unmaintained, no-warranty free software. +// Please use freely. It is appreciated (but by no means mandatory) to +// acknowledge the author's contribution. Thank you. +// Started on Thu Jun 26 23:29:03 1997 + +// +// Xlib tutorial: 2nd program +// Make a window appear on the screen and draw a line inside. +// If you don't understand this program, go to +// http://tronche.lri.fr:8000/gui/x/xlib-tutorial/2nd-program-anatomy.html +// + +#include // Every Xlib program must include this +#include // I include this to test return values the lazy way +#include // So we got the profile for 10 seconds + +#define NIL (0) // A name for the void pointer + +main() +{ + // Open the display + + Display *dpy = XOpenDisplay(NIL); + assert(dpy); + + // Get some colors + + int blackColor = BlackPixel(dpy, DefaultScreen(dpy)); + int whiteColor = WhitePixel(dpy, DefaultScreen(dpy)); + + // Create the window + + Window w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, + 200, 100, 0, blackColor, blackColor); + + // We want to get MapNotify events + + XSelectInput(dpy, w, StructureNotifyMask); + + // "Map" the window (that is, make it appear on the screen) + + XMapWindow(dpy, w); + + // Create a "Graphics Context" + + GC gc = XCreateGC(dpy, w, 0, NIL); + + // Tell the GC we draw using the white color + + XSetForeground(dpy, gc, whiteColor); + + // Wait for the MapNotify event + + for(;;) { + XEvent e; + XNextEvent(dpy, &e); + if (e.type == MapNotify) + break; + } + + // Draw the line + + XDrawLine(dpy, w, gc, 10, 60, 180, 20); + + // Send the "DrawLine" request to the server + + XFlush(dpy); + + // Wait for 10 seconds + + sleep(10); +} + diff --git a/xlib-tutorial/server.html b/xlib-tutorial/server.html new file mode 100644 index 0000000..a408834 --- /dev/null +++ b/xlib-tutorial/server.html @@ -0,0 +1,89 @@ + + +Xlib programming tutorial: What is all this "client-server" stuff ? + + + +

What does all this stuff about "client-server" mean ?

+ +Everybody says that X has a "client-server" architecture. So this must +be true, but what does it mean ? + +

+ +Well, basically a client-server architecture is conceptually a simple +thing, but the consequences may be a bit subtle, especially the way it +is implemented in the Xlib. + +

What is a client-server architecture ?

+ +A client-server architecture is a general mechanism for handling a +shared resource that several programs may want to access +simultaneously. In the case of X, the shared resources are the drawing +area and the input channel. If every process was allowed to write on +it at its will, several processes may want to draw at the same place, +resulting in an unpredictable chaos. Thus, only one process is allowed +to get access to the drawing area: the X server. The processes wanting +to draw stuff or get inputs send requests to the X servers (they are +"clients"). They do this over a communication channel. The X server +performs the requests for its clients, and sends them back replies. It +may also send messages without explicit client's requests to keep them +informed of what is going on. These messages sent by the server on its +own behalf are called "events". + +

Advantages of the client-server architecture

+ +The client-server architecture has several advantages, many of them +resulting from the ability to run the server and the clients on +separate machines. Here are some advantages: + +
    + +
  • A client-server architectured system can be very robust: since + the server runs in its own address space, it can protect itself + against poorly written clients. Thus, if a client has a bug, it + will crash alone, the server and the other clients still running + as if nothing has happened. + +
  • The client and the server don't have to run on the same machine, + so we have some communication mechanism here. + +
  • The client and the server may run on separate machines, resulting + in a better load distribution (possibly). + +
  • The client and the server don't have to run on the same hardware, + operating system, etc., giving a better interoperability. + +
+ +

Structure of the X client-server architecture

+ +As we already mentioned, the server and a client communicates over a +communication channel. This channel is composed of two layers: the +low-level one, which is responsible for carrying bytes in a reliable +way (that is with no loss nor duplication). This link may be among +others a named pipe in the Unix environment, a DECNet link and of +course a TCP/IP connection. + +

+ +The upper layer use the byte-transport channel to implement a +higher-level protocol: the X protocol. This protocol says how to tell +the server to request window creation, graphics drawing, and so on, +and how the server answers and sends events. The protocol itself is +separated into different parts: + +

    + +
  • How to connect and how to break a connection, +
  • how to represent the different data types, +
  • what are the requests and what they mean and +
  • what are the replies and what they mean. + +
+ +[to be continued]. + +
Christophe Tronche, ch.tronche@computer.org
+ + -- cgit v1.3