C/C++

[MEGOLDVA] Segítségkérés algebrai számoláshoz

Fórumok

Adott egy klasszikus Bresenham vonalhúzó. Ez hibaértéket számol az alfa meghatározásához, az indulópontban és a végpontban ez a hiba 0 (tehát az alfa itt 255, egyáltalán nem átlátszó), a két pont között pedig megfelelően számítódik (és abból az alfa az "a" változóba).

Ezt kellene átalakítani úgy, hogy vegyen figyelembe egy induló és érkező hibaértéket az alapján, hogy a koordináták alsó 8 bitje mi (ez a mostani kód csak azt a speciális esetet kezeli, amikor minden alsó 8 bit 0, ezt kellene általánosítani).
Megjegyzés: 256-tal felszorzott koordináták adódnak át (fix pontos aritmetika, alsó 8 bit: 0=0 egész pixelkoorináta, 127=0.5, 255=0.999999).

/* c[4] - szín, RGBA
 * x0,y0 - induló koordináta (alsó 8 bit a törtrész)
 * x1,y1 - vég koordináta (alsó 8 bit a törtrész)
 */
void line(uint8_t c[4], int32_t x0, int32_t y0, int32_t x1, int32_t y1)
{
    int sx, sy, x2, a;
    int dx, dy, err, e2;

    /* most csak levágjuk a törtrészt, hogy az eredeti Bresenhamnak egész pixelkoordinátákat adjunk át.
     * Levágás helyett kéne beleszámolni a törtészt az err, e2 léptetésébe */
    x0 /= 256;
    y0 /= 256;
    x1 /= 256;
    y1 /= 256;

    if(!c[3] || (x0 == x1 && y0 == y1)) return;

    sx = x0 < x1 ? 1 : -1;
    sy = y0 < y1 ? 1 : -1;
    dx = abs(x1-x0);
    dy = abs(y1-y0);

    err = dx*dx+dy*dy;
    e2 = err == 0 ? 1 : 0xffff7fl/sqrt(err);

    dx *= e2; dy *= e2; err = dx-dy;
    while(1) {
        a = err-dx+dy; if(a < 0) a = -a;
        a = 255 - (a >> 16); a = a * c[3] / 255;
        setpixel(x0, y0, c[0], c[1], c[2], a);
        e2 = err; x2 = x0;
        if(2*e2 >= -dx) {
            if(x0 == x1) break;
            if(e2+dy < 0xff0000l) {
                a = 255 - ((e2+dy) >> 16); a = a * c[3] / 255;
                setpixel(x0, y0 + sy, c[0], c[1], c[2], a);
            }
            err -= dy; x0 += sx;
        }
        if(2*e2 <= dy) {
            if(y0 == y1) break;
            if(dx-e2 < 0xff0000l) {
                a = 255 - ((dx-e2) >> 16); a = a * c[3] / 255;
                setpixel(x2 + sx, y0, c[0], c[1], c[2], a);
            }
            err += dx; y0 += sy;
        }
    }
}

Ha van kedve valakinek kibogarászni ezt, azért iszonyat hálás lennék (ha nagyon ráfeküdnék, biztos meg tudnám oldani, de az agyam most egy C fordító belső lelkivilágával van tele és nem akar átállni, folyton félreszámolom).
Egy Nyílt és Szabad Forráskódú projekthez kell, cserébe listáznám a megoldó nevét, mint kontributor és öregbíteném a hírnevét.

Forrásügyileg egyébként itt található, a hívása vonal esetén itt (ez csak felszorozza a koordinátákat 256-al), Bezier görbe esetén meg itt. Ez utóbbi rekurzívan felbontja az ívet több darab összefüggő vonalra, melyek már eshetnek törtpixelre. Viszont amikor ezeket a köztes koordinátákat átadom a vonalhúzónak, akkor kénytelen vagyok eldobni a törtrészt, mert most még nem kezeli azt. Ehhez kellene, hogy a törtrészből kiszámoljam az induló és vég hibaértéket, hogy aztán a Bresenham úgy húzza a vonalat, hogy már eleve anti-aliasolt pixelekkel indítson, ami most csak a ciklus közepén történik.

EDIT: Leegyszerűsítettem a kiírást, és belinkeltem, hogy pontosan mihez kell.

----------------------------------------------------------------------------------------------------------------------------------------------------------
MEGOLDÁS
Köszönöm mindenkinek a segítséget és a próbálkozásokat! Végül is megoldottam úgy, ahogy eredetileg elképzeltem. Jó sok printf kellett hozzá, hogy megtaláljam a számolási hibát, de meglett.

A lépések:
1. nem lett volna szükséges, de végül is megcseréltem a koordinátákat, hogy ne kelljen sx, sy változó meg abs()
2. átalakítottam, hogy a Bresenham fix pontos aritmetikát használjon (összeadás okés, de szorzásnál ugye le kell osztani 256-al)
3. a Descartes távolságot és a hibaértéket is 256-od törtpixelekre határozza így meg, nem egészpixelekre (A..B)
4. a ciklus léptetését átállítottam 256-ra, így továbbra is egész pixelszámú lépést hajt végre
5. a ciklus végét továbbra is egész pixelekre nézem, így a vége hibaérték kezelése automatikus (A..|B|)
6. az induló hibaértéket meg csak simán hozzáadtam az indoló err értékéhez ((A+hiba)..|B|)

void line(uint8_t c[4], int x0, int y0, int x1, int y1)
{
    int64_t dx, dy, err, e, e2;
    int x2, y2, ex, ey, a;

    if(!c[3] || (x0 / 256 == x1 / 256 && y0 / 256 == y1 / 256)) return;

    if(x1 < x0) { a = x0; x0 = x1; x1 = a; }
    if(y1 < y0) { a = y0; y0 = y1; y1 = a; }

    dx = x1 - x0; dy = y1 - y0; a = dx >> 8; e = dy >> 8;
    err = ((dx*dx) >> 8) + ((dy*dy) >> 8);
    e2 = err == 0 ? 1 : 0xffff7f / sqrt(a*a + e*e);
    dx *= e2; dy *= e2; err = (dx + ((x0 & 0xff) << 24)) - (dy - ((y0 & 0xff) << 24));
    while(1) {
        x2 = x0 >> 8; y2 = y0 >> 8;
        a = err - dx + dy; if(a < 0) a = -a;
        setpixel(x2, y2, c[0], c[1], c[2], (255 - (a >> 24)) * c[3] / 255);
        e2 = 2 * err; e = err;
        if(e2 >= -dx) {
            if(x2 == ex) break;
            if(e+dy < 0xff000000)
                setpixel(x2, y2 + 1, c[0], c[1], c[2], (255 - ((e+dy) >> 24)) * c[3] / 255);
            err -= dy; x0 += 256;
        }
        if(e2 <= dy) {
            if(y2 == ey) break;
            if(dx-e < 0xff000000)
                setpixel(x2 + 1, y2, c[0], c[1], c[2], (255 - ((dx-e) >> 24)) * c[3] / 255);
            err += dx; y0 += 256;
        }
    }
}

Így a végeredmény a teljes törtpixeles távolságra számítódik, de eltolással indul és hamarabb érhet véget, pont, ahogy akartam. Egész pixeles koordinátákra pedig pontosan ugyanazokat az értékeket adja, mint az eredeti Bresenham.

A legszebb az egészben, hogy ez az megoldás csak a ciklus előtt tér el lényegesen, a ciklusmagon belül pontosan ugyanazok és ugyanannyi művelet található, így tehát nem lassít rajta semmit (na jó, kicsit optimalizáltam az eredetihez képest a fenti megoldásban, de értitek).

[MEGOLDVA] wine alatt windows c programban az fflush nem megy

Fórumok

Sziasztok!

Kicsit részletesebben a tárgyban leírtak:

Van egy 3D Gamestudio nevű windows-os fejlesztő környezet amit wine-ban futtatok. Ebben készítek egy házikedvenc programot. A környezetben a Lite-C nyelvet lehet használni. Ez a program nyelve.

A programban van egy rész ami log fájlt készít és az fflush funkciót használja arra az esetre, ha elszállna az alkalmazás, de még volna kiírandó sor a log-ba.

A rendszer (wine + 3D GS) jól működött amíg egyszer a system update (volt ott minden mint a búcsúban) után bemondta az unalmast és végtelen ciklusba került/lefagyott. A debug során kiderült, hogy az fflush hívásán akad el.

Ebben szeretnék segítséget kapni, hogyan lehetne felülkeredni a problémán. Próbáltam downgrade-t, meg 64 bites helyett 32 bites prefixet használni, különféle library-ket felrakni. Egyik sem segített, bár lehet nem a megfelelővel próbálkoztam. Az fflush egyébként az msvcrt.dll-ben található, ha ez számít.

Az OS amit használok Manjaro 24.0.4 Wynsdey, Kernel: x86_64 Linux 6.9.9-1-MANJARO.
wine verzióL: 9.12

Előre is köszönet mindenkinek aki segíteni próbál.

C, C++, E?

Fórumok

Debian rendszerben letöltöttem néhány minimális konverziós utility-t, ami nagyon hasznos lenne számomra. A többsége egyszerű C program, és sikeresen le is fordulnak, de van köztük pár ".e" kiterjesztésű. Ezekkel nem tudok mit kezdeni. Próbáltam, utánakeresni, lefordítani gcc, g++ segítségével, de egyelőre sikertelenül.

Van esélyem ezeket a fájlokat lefordítani különösebb varázslat nélkül? Mi ez az E egyáltalán? Ez tényleg valami C++ fejlesztés, mint a D? (D programokat már írtam, de ahhoz külön fordító kellett.)

Tud valaki segíteni, hogy ezt a pár ".e" konvertert le tudjam fordítani linux környezetben?

Minden információt megköszönök a témában!

Egyszerű, multiplatform UI toolkit

Fórumok

Kerestem egy egyszerűen használható UI toolkit-et ANSI C-ben, és a Nuklear-t találtam (a C++ megfelelője az ImGui, de az nem használható ANSI C-ből).

Nem tudom, hányan ismeritek ezeket, de én nagyon felhúztam magam rajtuk, csupa hazugság a Nuklear. Állítólag kicsi (hát rohadtul nem, a 18000 SLoC az nagyon nem kevés), állítólag hatékony (hát rohadtul nem, folyamatosan memóriát allokálgat és minden képkockánál kovertálgatni kell OpenGL esetén), és állítólag könnyen integrálható (hát rohadtul nem, gyakorlatilag minden glue kódot a nulláról magadnak kell megírni). Ja, és ez az immediate-mode konkrétan egy használhatatlan koncepció, multithreading esélytelen vele például. Ja, és még egy: kurvára nem is C89-ben íródott, hazudik a a README-je...

Szóval, hogy rövidre fogjam, csináltam egy UI toolkitet, ami valóban tudja mindazt, amit a Nuklear igér, de képtelen teljesíteni.
SMGUI

- Státusz-Módú GUI (nem immediate-mode és nem is callback-driven, csak hivatkozni kell a már meglévő változóidra és kész)
- egyaránt használható ANSI C és C++ forrásokból is
- Egyetlen, függőségmentes fejlécfájl (plusz opcionális modulok)
- Platform és backend független (a repóban GLFW3, SDL2/3 és X11 illesztő modulok találhatók, ezek tartalmazzák a glue kódot)
- Bármilyen fontot képes kezelni (a repóban PC Screen Font (amit a Linux Console használ) és vektor fontokhoz SSFN modul található)
- Kis kódbázis (tényleg, mindössze kb. 2500 SLoC)
- Kevés memóriát eszik (gyakorlatilag csak ablak átméretezésnél allokál memóriát, képernyő méretűt és ennyi)
- Könnyen integrálható (kb. 5 sornyi kóddal)
- HTML flow-szerű, flexibilis elrendezés
- Bővíthető a kódodból új widgetekkel (példának egy On-Screen Keyboard-os szövegbeviteli mező van a repóban)
- UTF-8 és többnyelvűség támogatás (a nyelv akár menet közben is változtatható)
- teljes UNICODE támogatás (ami a Nuklear font bakerével képtelenség, túl sok az a 0x10FFFF darab glif egyetlen textúrához)
- multithreading támogatás (ami immediate-mode esetén képtelenség)
- természetesen magyar nyelvű doksival: https://bztsrc.gitlab.io/smgui/index.hu.html

A licensze MIT, szóval szabadon beépíthető akár fizetős alkalmazásokba is.

[MEGOLDVA] Milyen rand algoritmust használ a C++ Windows alatt?

Fórumok

Újabb hülye kérdés a részemről :-) Szóval, melyik pszeudo-random algoritmust használja a Windows alatt az MSVC-vel fordított C++ srand() / rand()?

Kicsit nyomoztam, az MS doksiban azt írták, hogy a Mersenne Twister egyik változata, az MT19937, de nem (ez alapján nem ugyanaz, legalábbis mingw-vel). A másik helyen meg azt olvastam, hogy C++11 óta a Park-Miller, de az sem. Sőt, nem is a Lehner LCG. Na de akkor melyik? Nem az, ami a Linux-os glibc beli, az biztos. Egyébként az is kérdés, van különbség Windows alatt a C-beli rand() és a C++-beli között? Mármint látszatra nem ugyanaz az implementációjuk, de ha C-ben és C++-ban is lefordítok egy minimál programot, akkor ugyanaz az eredményük. Létezik, hogy itt még a mingw is bekavarhat? Mármint a a mingw a Windows-os rand()-ot használja egyáltalán, vagy saját implementációja van abban a libstdc++-ban, amit behúz? Nem azonos a Linux-os glibc-vel, az tuti, de abban nem vagyok biztos, hogy az MSVC-fel fordítottal azonos-e egyáltalán.

Ha számít a fordító és a nyelv is, akkor pontosítok: az MSVC C++ fordító által, a "windows.h" fejléc behúzásakor elérhető srand() és rand() algoritmusa kellene. (Itt egyénként az sem világos, hogy ez a "random" fejlécet, avagy a "cstdlib" fejlécet húzza-e be, merthogy mindkettőben van egy rand(), és nem azonosak.)

És hogy miért kéne: a gondom az, hogy egy jelenlegi Windows-only megoldást próbálok cross-platformként reprodukálni, ami egy voxel világot generál. Sajnos az algoritmusa rand()-ot használ, így hiába ugyanaz a seed érték, Windows és Linux alatt tök más az eredmény, ami nem jó. Ezért akarom kicserélni Linux alatt az srand()-ot és rand()-ot pontosan ugyanarra az implementációra, amit a Windows használ, hogy hordozható legyen az eredmény. Remélem érhető.

Szerk: bakker, mekkora mázlim van, véletlenül belenyúltam a a tutiba az egyik stackoverflow oldal eldugott komment szekciójában! Nem a Lehner, nem is a Park-Miller, hanem egy 1958-as LCG variációt használ, konkrétan ezt:

static int random_seed;

void msvc_cpp_srand(int seed) {
    random_seed = seed;
}

int msvc_cpp_rand() {
    random_seed = (random_seed * 214013 + 2531011) & 0xFFFFFFFF;
    return (random_seed >> 16) & 0x7FFF;
}

Azt továbbra sem tudom, hogy a "windows.h" akkor most a cstdlib-et vagy a random-ot használja-e (illetve azt már tudom, hogy a mingw egyiket sem, külön állatfaj), de már nem is fontos, mivel megvan ez a két konkrét függvény :-)

----------------------------------------------------------------------------------------------------------------------
PONTOSÍTÁS

Szükségét látom tisztázni, mert sokakat félrevezetett (elsőre engem is), hogy az eredeti programban nincs véletlenszám generálás, sőt mi több, valódi véletlenszámokkal garantáltan működésképtelen lenne.

Amire valójában szüksége van, az egy megbízható, kiszámítható bitshuffler. Az, hogy az eredeti fejlesztő erre a rand()-ot használta, csak mert az épp akkor aktuális cstdlib-jében az egy bitshufflerként volt/van implementálva, na az a csillámfaszlámaság csimborasszója.

[MEGOLDVA] Újabb Clang bug

Fórumok

Megpróbálom érthetően leírni, hogy mi is a gond, nem triviális.

Szóval, a régebbi Clang fordítók, és a distro-mban évő 17.0.6 is freestanding módban helyesen .text, .data és .bss szekciókat generál.

Azonban a legfrissebb (forrásból telepített) Clang nem, az ilyent eredményez:

readelf -S exfat.o 
There are 9 section headers, starting at offset 0x1968:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .strtab           STRTAB           0000000000000000  00001830
       0000000000000132  0000000000000000           0     0     1
  [ 2] .text             PROGBITS         0000000000000000  00000040
       0000000000000d10  0000000000000000  AX       0     0     16
  [ 3] .rela.text        RELA             0000000000000000  00001110
       0000000000000708  0000000000000018           8     2     8
  [ 4] .note             NOTE             0000000000000000  00000d50
       0000000000000084  0000000000000000   A       0     0     4
  [ 5] .comment          PROGBITS         0000000000000000  00000dd4
       0000000000000020  0000000000000001  MS       0     0     1
  [ 6] .note.GNU-stack   PROGBITS         0000000000000000  00000df4
       0000000000000000  0000000000000000           0     0     1
  [ 7] .llvm_addrsig     LOOS+0xfff4c03   0000000000000000  00001818
       0000000000000018  0000000000000000   E       8     0     1
  [ 8] .symtab           SYMTAB           0000000000000000  00000df8
       0000000000000318  0000000000000018           1     7     8
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), l (large), p (processor specific)

Amint látható, BSS szekciónak hűlt helye, van viszont helyette LOOS szekció (ami OS-specifikust jelent), és aminek freestanding módban nem is szabadna léteznie, tehát ez egyértelműen Clang fordító bug.

A kérdésem az, hogy próbáltam a doksit bújni, de nem találtam, viszont hátha valaki itt a HUP-on ismer olyan Clang parancssori kapcsolót, esetleg fordítási opciót, amivel a korábbi működés helyreállítható (azaz hogy freestanding módban NE legyenek OS-specifikus szekciók, csak a hagyományos .text, .data és .bss).

Bármi tipp?

MEGOLDÁS: ezer hála lacos-nak, aki megtalálta az egyik kapcsolót! A hiányzó bss-re is találtam megoldást, az -fno-addrsig -fno-common kapcsolók együttesen eltűntetik a LOOS szekciót és visszahozzák a BSS-t, azaz egy-az-egyben visszaállítják a korábbi működést. Azt nem értem, hogy freestanding módban miért nem ezek az alapértelmezettek, de sebaj, megadható, működik!

X11 matatás Xlib nélkül

Fórumok

Érdekes szösszenet, bár hasznát venni sosem fogom, de mégis lekötött, szerintem roppant tanulságos.

https://hereket.com/posts/from-scratch-x11-windowing/

Hogyan nyissunk X11 ablakot és írjuk rá, hogy "Helló világ", majd fogadjunk event-eket, mindössze 200 sornyi C kódból, bármiféle lib vagy header használata nélkül, csakis közvetlen socket írás / olvasással (POSIX headerök és libc azért kell neki, de semmi más).

#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>


int32_t GlobalId = 0;
int32_t GlobalIdBase = 0;
int32_t GlobalIdMask = 0;
int32_t GlobalRootWindow = 0;
int32_t GlobalRootVisualId = 0;

int32_t GlobalTextOffsetX = 10;
int32_t GlobalTextOffsetY = 20;

#define READ_BUFFER_SIZE 16*1024

#define RESPONSE_STATE_FAILED 0
#define RESPONSE_STATE_SUCCESS 1
#define RESPONSE_STATE_AUTHENTICATE 2

#define X11_REQUEST_CREATE_WINDOW 1
#define X11_REQUEST_MAP_WINDOW 8
#define X11_REQUEST_IMAGE_TEXT_8 76
#define X11_REQUEST_OPEN_FONT 45
#define X11_REQUEST_CREATE_GC 55


#define X11_EVENT_FLAG_KEY_PRESS 0x00000001
#define X11_EVENT_FLAG_KEY_RELEASE 0x00000002
#define X11_EVENT_FLAG_EXPOSURE 0x8000


#define WINDOWCLASS_COPYFROMPARENT 0
#define WINDOWCLASS_INPUTOUTPUT 1
#define WINDOWCLASS_INPUTONLY 2

#define X11_FLAG_BACKGROUND_PIXEL 0x00000002 
#define X11_FLAG_WIN_EVENT 0x00000800 

#define X11_FLAG_FG 0x00000004
#define X11_FLAG_BG 0x00000008
#define X11_FLAG_FONT 0x00004000
#define X11_FLAG_GC_EXPOSURE 0x00010000

#define PAD(N) ((4 - (N % 4)) % 4)

void VerifyOrDie(int IsSuccess, const char *Message) {
    if(!IsSuccess)  {
        fprintf(stderr, "%s", Message);
        exit(13);
    }
}

void VerifyOrDieWidthErrno(int IsSuccess, const char *Message) {
    if(!IsSuccess)  {
        perror(Message);
        exit(13);
    }
}

void DumpResponseError(int Socket, char* ReadBuffer) {
        uint8_t ReasonLength = ReadBuffer[1];
        uint16_t MajorVersion = *((uint16_t*)&ReadBuffer[2]);
        uint16_t MinorVersion = *((uint16_t*)&ReadBuffer[4]);
        uint16_t AdditionalDataLength = *((uint16_t*)&ReadBuffer[6]); // Length in 4-byte units of "additional data"
        uint8_t *Message = (uint8_t*)&ReadBuffer[8];

        int BytesRead = read(Socket, ReadBuffer + 8, READ_BUFFER_SIZE-8);

        printf("State: %d\n", ReadBuffer[0]);
        printf("MajorVersion: %d\n", MajorVersion);
        printf("MinorVersion: %d\n", MinorVersion);
        printf("AdditionalDataLength: %d\n", AdditionalDataLength);
        printf("Reason: %s\n", Message);
}

void AuthenticateX11() {
    fprintf(stderr, "Current version of the app does not support authentication.\n");
    fprintf(stderr, "Please run 'xhost +local:' in your terminal to disable cookie based authentication\n");
    fprintf(stderr, "and allow local apps to communication with Xorg without it.");
}

int32_t GetNextId() {
    int32_t Result = (GlobalIdMask & GlobalId) | GlobalIdBase;
    GlobalId += 1;
    return Result;
}

void PrintResponseError(char *Data, int32_t Size) {
    char ErrorCode = Data[1];
    const char *ErrorNames[] = {
        "Unknown Error",
        "Request",
        "Value",
        "Window",
        "Pixmap",
        "Atom",
        "Cursor",
        "Font",
        "Match",
        "Drawable",
        "Access",
        "Alloc",
        "Colormap",
        "GContext",
        "IDChoice",
        "Name",
        "Length",
        "Implementation",
    };

    const char* ErrorName = "Unknown error";
    if(ErrorCode < sizeof(ErrorNames) / sizeof(ErrorNames[0])) {
        ErrorName = ErrorNames[ErrorCode];
    }

    
    uint16_t Minor = *((uint16_t*)&Data[8]);
    uint8_t Major = *((uint8_t*)&Data[10]);

    printf("\033[0;31m");
    printf("Response Error: [%d] %s", ErrorCode, ErrorName);
    printf("	Minor: %d, Major: %d", Minor, Major);
    printf("\033[0m\n");


}

void PrintAndProcessEvent(char *Data, int32_t Size) {
    char EventCode = Data[0];
    const char* EventNames[] = {
        "-- Wrong Event Code --",
        "-- Wrong Event Code --",
        "KeyPress",
        "KeyRelease",
        "ButtonPress",
        "ButtonRelease",
        "MotionNotify",
        "EnterNotify",
        "LeaveNotify",
        "FocusIn",
        "FocusOut",
        "KeymapNotify",
        "Expose",
        "GraphicsExposure",
        "NoExposure",
        "VisibilityNotify",
        "CreateNotify",
        "DestroyNotify",
        "UnmapNotify",
        "MapNotify",
        "MapRequest",
        "ReparentNotify",
        "ConfigureNotify",
        "ConfigureRequest",
        "GravityNotify",
        "ResizeRequest",
        "CirculateNotify",
        "CirculateRequest",
        "PropertyNotify",
        "SelectionClear",
        "SelectionRequest",
        "SelectionNotify",
        "ColormapNotify",
        "ClientMessage",
        "MappingNotify",
    };

#define REPLY_EVENT_CODE_KEY_PRESS 2
#define REPLY_EVENT_CODE_EXPOSE 12

const char* TERMINAL_TEXT_COLOR_RED = "\033[0;32m";
const char* TERMINAL_TEXT_COLOR_CLEAR = "\033[0m";

    if(EventCode == REPLY_EVENT_CODE_EXPOSE) {
        // NOTE: Exposure event
        const char *EventName = "Expose";
        uint16_t SequenceNumber = *((uint16_t*)&Data[2]);
        uint32_t Window = *((uint32_t*)&Data[4]);
        uint16_t X = *((uint16_t*)&Data[8]);
        uint16_t Y = *((uint16_t*)&Data[10]);
        uint16_t Width = *((uint16_t*)&Data[12]);
        uint16_t Height = *((uint16_t*)&Data[14]);
        uint16_t Count = *((uint16_t*)&Data[16]);

        printf(TERMINAL_TEXT_COLOR_RED);
            printf("%s: ", EventName);
        printf(TERMINAL_TEXT_COLOR_CLEAR);

        printf("Seq %d, ", SequenceNumber);
        printf("Win %d: ", Window);
        printf("X %d: ", X);
        printf("Y %d: ", Y);
        printf("Width %d: ", Width);
        printf("Height %d: ", Height);
        printf("Count %d: ", Count);
        printf("\n");
        /* printf("%s: Seq %d\n", EventName, SequenceNumber); */
    } else if(EventCode == REPLY_EVENT_CODE_KEY_PRESS) {
        const char *EventName = "KeyPress";
        char KeyCode = Data[1];
        uint16_t SequenceNumber = *((uint16_t*)&Data[2]);
        uint32_t TimeStamp = *((uint32_t*)&Data[4]);
        uint32_t RootWindow = *((uint32_t*)&Data[8]);
        uint32_t EventWindow = *((uint32_t*)&Data[12]);
        uint32_t ChildWindow = *((uint32_t*)&Data[16]); // NOTE: Always 0
        int16_t RootX = *((int16_t*)&Data[20]);
        int16_t RootY = *((int16_t*)&Data[22]);
        int16_t EventX = *((int16_t*)&Data[24]);
        int16_t EventY = *((int16_t*)&Data[26]);
        int16_t SetOfKeyButMask = *((int16_t*)&Data[28]);
        int8_t IsSameScreen = *((int8_t*)&Data[30]);

        printf(TERMINAL_TEXT_COLOR_RED);
            printf("%s: ", EventName);
        printf(TERMINAL_TEXT_COLOR_CLEAR);

        // NOTE: Temporary hack that will not work everywhere
        int StepSize = 10;
        if(KeyCode == 25) { GlobalTextOffsetY += StepSize; }
        if(KeyCode == 39) { GlobalTextOffsetY -= StepSize; }
        if(KeyCode == 38) { GlobalTextOffsetX -= StepSize; }
        if(KeyCode == 40) { GlobalTextOffsetX += StepSize; }

        printf("Code %u, ", (uint8_t)KeyCode);
        printf("Seq %d, ", SequenceNumber);
        printf("Time %d, ", TimeStamp);
        printf("Root %d, ", RootWindow);
        printf("EventW %d, ", EventWindow);
        printf("Child %d, ", ChildWindow);
        printf("RX %d, ", RootX);
        printf("RY %d, ", RootY);
        printf("EX %d, ", EventX);
        printf("EY %d, ", EventY);
        printf("\n");
    } else {
        const char* EventName = " - Unknown Event Code -";
        if(EventCode < sizeof(EventNames) / sizeof(EventNames[0])) {
            EventName = EventNames[EventCode];
        }
        // printf("-------------Event: %s\n", EventName);
        // for(int i = 0; i < Size; i++) {
            // printf("%c", Data[i]);
        // }
        // printf("\n");
    }

}

void GetAndProcessReply(int Socket) {
    char Buffer[1024] = {};
    int32_t BytesRead = read(Socket, Buffer, 1024);

    uint8_t Code = Buffer[0];

    if(Code == 0) {
        PrintResponseError(Buffer, BytesRead);
    } else if (Code == 1) {
        printf("---------------- Unexpected reply\n");
    } else {
        // NOTE: Event?
        PrintAndProcessEvent(Buffer, BytesRead);
    }
}

int X_InitiateConnection(int Socket) { 
    // TODO: Remove global variables and put them into 'connection' struct.
    int SetupStatus = 1;
    char SendBuffer[16*1024] = {};
    char ReadBuffer[16*1024] = {};

    uint8_t InitializationRequest[12] = {};
    InitializationRequest[0] = 'l';
    InitializationRequest[1] = 0;
    InitializationRequest[2] = 11;

    int BytesWritten = write(Socket, (char*)&InitializationRequest, sizeof(InitializationRequest));
    VerifyOrDie(BytesWritten == sizeof(InitializationRequest), "Wrong amount of bytes written during initialization");

    int BytesRead = read(Socket, ReadBuffer, 8);

    if(ReadBuffer[0] == RESPONSE_STATE_FAILED) {
        DumpResponseError(Socket, ReadBuffer);
    }
    else if(ReadBuffer[0] == RESPONSE_STATE_AUTHENTICATE) {
        AuthenticateX11();
    }
    else if(ReadBuffer[0] == RESPONSE_STATE_SUCCESS) {
        printf("INIT Response SUCCESS. BytesRead: %d\n", BytesRead);

        BytesRead = read(Socket, ReadBuffer + 8, READ_BUFFER_SIZE-8);
        printf("---------------------------%d\n", BytesRead);

        /* -------------------------------------------------------------------------------- */
        uint8_t _Unused = ReadBuffer[1];
        uint16_t MajorVersion = *((uint16_t*)&ReadBuffer[2]);
        uint16_t MinorVersion = *((uint16_t*)&ReadBuffer[4]);
        uint16_t AdditionalDataLength = *((uint16_t*)&ReadBuffer[6]); // Length in 4-byte units of "additional data"

        uint32_t ResourceIdBase = *((uint32_t*)&ReadBuffer[12]);
        uint32_t ResourceIdMask = *((uint32_t*)&ReadBuffer[16]);
        uint16_t LengthOfVendor = *((uint16_t*)&ReadBuffer[24]);
        uint8_t NumberOfFormants = *((uint16_t*)&ReadBuffer[29]);
        uint8_t *Vendor = (uint8_t *)&ReadBuffer[40];

        int32_t VendorPad = PAD(LengthOfVendor);
        int32_t FormatByteLength = 8 * NumberOfFormants;
        int32_t ScreensStartOffset = 40 + LengthOfVendor + VendorPad + FormatByteLength;

        uint32_t RootWindow = *((uint32_t*)&ReadBuffer[ScreensStartOffset]);
        uint32_t RootVisualId = *((uint32_t*)&ReadBuffer[ScreensStartOffset + 32]);

        GlobalIdBase = ResourceIdBase;
        GlobalIdMask = ResourceIdMask;
        GlobalRootWindow = RootWindow;
        GlobalRootVisualId = RootVisualId;

        SetupStatus = 0;
    }

    return SetupStatus;
}

int X_CreatWindow(int Socket, int X, int Y, int Width, int Height) {
    // TODO: Put this into 'connection' struct
    char SendBuffer[16*1024] = {};
    char ReadBuffer[16*1024] = {};

    int32_t WindowId = GetNextId();
    int32_t Depth = 0;
    uint32_t BorderWidth = 1;
    int32_t CreateWindowFlagCount = 2;
    int RequestLength = 8+CreateWindowFlagCount;

    SendBuffer[0] = X11_REQUEST_CREATE_WINDOW;
    SendBuffer[1] = Depth;
    *((int16_t *)&SendBuffer[2]) = RequestLength;
    *((int32_t *)&SendBuffer[4]) = WindowId;
    *((int32_t *)&SendBuffer[8]) = GlobalRootWindow;
    *((int16_t *)&SendBuffer[12]) = X;
    *((int16_t *)&SendBuffer[14]) = Y;
    *((int16_t *)&SendBuffer[16]) = Width;
    *((int16_t *)&SendBuffer[18]) = Height;
    *((int16_t *)&SendBuffer[20]) = BorderWidth;
    *((int16_t *)&SendBuffer[22]) = WINDOWCLASS_INPUTOUTPUT;
    *((int32_t *)&SendBuffer[24]) = GlobalRootVisualId;
    *((int32_t *)&SendBuffer[28]) = X11_FLAG_WIN_EVENT | X11_FLAG_BACKGROUND_PIXEL;
    *((int32_t *)&SendBuffer[32]) = 0xff000000;
    *((int32_t *)&SendBuffer[36]) = X11_EVENT_FLAG_EXPOSURE | X11_EVENT_FLAG_KEY_PRESS;

    int BytesWritten = write(Socket, (char *)&SendBuffer, RequestLength*4);

    return WindowId;
}

int X_MapWindow(int Socket, int WindowId) {
    // TODO: Put this into 'connection' struct
    char SendBuffer[16*1024] = {};
    char ReadBuffer[16*1024] = {};

    SendBuffer[0] = X11_REQUEST_MAP_WINDOW;
    SendBuffer[1] = 0;
    *((int16_t *)&SendBuffer[2]) = 2;
    *((int32_t *)&SendBuffer[4]) = WindowId;

    int BytesWritten = write(Socket, (char *)&SendBuffer, 2*4);
    return 0;
}

void X_OpenFont(int32_t Socket, char *FontName, int32_t FontId) {
    char SendBuffer[16*1024] = {};
    char ReadBuffer[16*1024] = {};
    int BytesWritten = 0;
    int BytesRead = 0;

    int32_t FontNameLength = strlen((char *)FontName);
    int32_t Pad = PAD(FontNameLength);
    int RequestLength = (3 + (FontNameLength + Pad)/4);

    SendBuffer[0] = X11_REQUEST_OPEN_FONT;
    SendBuffer[1] = 0;
    *((uint16_t *)&SendBuffer[2]) = RequestLength;
    *((uint32_t *)&SendBuffer[4]) = FontId;
    *((uint16_t *)&SendBuffer[8]) = FontNameLength;
    strncpy(SendBuffer + 12, (char *)FontName, FontNameLength);

    int32_t WriteSize = 12 + FontNameLength + Pad;
    BytesWritten = write(Socket, (char *)&SendBuffer, WriteSize);
}

void X_CreateGC(int32_t Socket, int32_t GcId, int32_t FontId) {
    char SendBuffer[16*1024] = {};

    int32_t CreateGcFlagCount = 3;
    int RequestLength = 4 + CreateGcFlagCount;

    SendBuffer[0] = X11_REQUEST_CREATE_GC;
    SendBuffer[1] = 0;
    *((int16_t *)&SendBuffer[2]) = RequestLength;
    *((int32_t *)&SendBuffer[4]) = GcId;
    *((int32_t *)&SendBuffer[8]) = GlobalRootWindow;
    *((int32_t *)&SendBuffer[12]) = X11_FLAG_FG | X11_FLAG_BG | X11_FLAG_FONT;
    *((int32_t *)&SendBuffer[16]) = 0xFF00FF00; // Foreground
    *((int32_t *)&SendBuffer[20]) = 0xFF000000; // Background
    *((int32_t *)&SendBuffer[24]) = FontId; // Font

    write(Socket, (char *)&SendBuffer, RequestLength*4);
}

void WriteText(int Socket, int WindowId, int GCid, int16_t X, int16_t Y, const char *Text, int32_t TextLength) {
    char Buffer[16*1024] = {};

    uint32_t ContentLength = 4 + (TextLength + PAD(TextLength))/4;

    Buffer[0] = (uint8_t)X11_REQUEST_IMAGE_TEXT_8;
    Buffer[1] = TextLength;
    *((int16_t *)&Buffer[2]) = ContentLength; 
    *((int32_t *)&Buffer[4]) = WindowId;
    *((int32_t *)&Buffer[8]) = GCid;
    *((int16_t *)&Buffer[12]) = X; 
    *((int16_t *)&Buffer[14]) = Y; 

    strncpy(&Buffer[16], (char *)Text, TextLength);
    int BytesWritten = write(Socket, (char *)&Buffer, ContentLength*4);
}

int main(){
    int Socket = socket(AF_UNIX, SOCK_STREAM, 0);
    VerifyOrDie(Socket > 0, "Couldn't open a socket(...)");

    struct sockaddr_un Address;
    memset(&Address, 0, sizeof(struct sockaddr_un));
    Address.sun_family = AF_UNIX;
    strncpy(Address.sun_path, "/tmp/.X11-unix/X0", sizeof(Address.sun_path)-1);

    int Status = connect(Socket, (struct sockaddr *)&Address, sizeof(Address));
    VerifyOrDieWidthErrno(Status == 0, "Couldn't connect to a unix socket with connect(...)");

    int SetupStatus = X_InitiateConnection(Socket);

    if(SetupStatus == 0) {
        int32_t X = 100;
        int32_t Y = 100;
        uint32_t Width = 600;
        uint32_t Height = 300;
        int WindowId = X_CreatWindow(Socket, X, Y, Width, Height);

        X_MapWindow(Socket, WindowId);

        int32_t FontId = GetNextId();
        X_OpenFont(Socket, (int8_t *)"fixed", FontId);

        int32_t GcId = GetNextId();
        X_CreateGC(Socket, GcId, FontId);

        struct pollfd PollDescriptors[1] = {};
        PollDescriptors[0].fd = Socket;
        PollDescriptors[0].events = POLLIN;
        int32_t DescriptorCount = 1;
        int32_t IsProgramRunning = 1;
        while(IsProgramRunning){
            int32_t EventCount = poll(PollDescriptors, DescriptorCount, -1);

            if(PollDescriptors[0].revents & POLLERR) {
                printf("------- Error\n");
            }

            if(PollDescriptors[0].revents & POLLHUP) {
                printf("---- Connection close\n");
                IsProgramRunning = 0;
            }

            char* t1 = "Hello, World!";
            char* t2 = "This is a test text directly written to X";
            char* t3 = "Whooha. Is this even legal? Let's keep a secret!";
            WriteText(Socket, WindowId, GcId, GlobalTextOffsetX, GlobalTextOffsetY, t1, strlen(t1));
            WriteText(Socket, WindowId, GcId, GlobalTextOffsetX, GlobalTextOffsetY + 15, t2, strlen(t2));
            WriteText(Socket, WindowId, GcId, GlobalTextOffsetX, GlobalTextOffsetY + 30, t3, strlen(t3));

            GetAndProcessReply(PollDescriptors[0].fd);
        }
    }

}

Fordítás:

gcc main.c -o main

Ennyi! A linkelt blogposztban részletesen el van magyarázva minden, érdemes elolvasni hozzá.

3D modellező egy hét alatt

Fórumok

Nem semmi a csóka, 1 hét alatt összedobott egy komplett 3D modellező programot. Mivel C-ben írta, így gond nélkül wasm-ra is fordítható, és böngészőben is fut. Mindezt szépen le is dokumentálta, tecső videóval, bloggal stb.
Külön érdekesség, hogy a videóban azt is elmondja, miért választotta a Signed Distance Field-et a mesh helyett, és remek animációkkal még a működési elvét is elmagyarázza, roppant tanulságos!

ví dejó: https://youtu.be/-Xb3Kk3HhIw (akkor is érdemes megnézni, ha nem érdekel a megvalósítás)
webdemó: https://danielchasehooper.com/projects/shapeup/ (interaktív, kipróbálható)
forrás: https://github.com/danielchasehooper/ShapeUp-public
blog: https://danielchasehooper.com/posts/shapeup/ (itt belemegy az implementációs részletekbe is)

A blogon egyébként arra is kitér, hogy miért a C-t választotta. SPOILER: pont azokat az érveket hozza fel, amiket már én is jó ideje hangoztatok, pedig nem valószínű. hogy ez a Daniel gyerek sokat olvasná a HUP-ot :-D

És mindezt tokkal vonóval 1 hét alatt! (Ebben benne van a videó elkészítése is, ami állítólag tovább tartott, mint a programot megírni)

STM32 arm debuggolás

Fórumok

Bare metal szintről próbálok beletanulni az ST arm alapú processzorainak debuggolásának. A fejlesztő rendszer egy Debian 12 gépen fut.
Az első a tool chain és aztán a jó öreg "blinky" és miután ez működik jöhet a debugger.. Miután fe
Több bare metal ismertetőt átolvastam mindegyik arra fut ki, hogy addjam ki a parancsot:

$ arm-none-eabi-gdb main.elf

Úgy tűnik ez elavult, a tool chain megvan, azonban a gdb ügyében a Debian elküld, hogy telepítsem fel az arm architecturát. Csináltam már ilyesmit, de konkrétan a debuggolás viszonylatában nem :(
Nem találok hozzá leírást.

Ha feltelpítem az arm architecturát hogy indíthatok egy debug sessiont?

STM32F051 CMSIS bare metal header files

Fórumok

Bare metal programming -hoz keresem a megfelelő CMSIS fájlokat, arm-none-eabi tool chain, de eltévedtem az erdőben.

STM32F051 -es MCU-hoz keresem a megfelelő CMSIS fájlokat. Az egyik git repoban találtam olyat, hogy stm32f031x6.h (ezzel sikerült is egy kínai gyártmányú kis panelt a blink-ig felébreszteni).
A google a developper.arm.com -ot dobja fel, de az mintha a keil-hez fűződne ill. valami olyan ide-hez ami kezeli a (számomra szokatlan) .pack fájlt.
Megint a google feldob egy kimondottan az STMicroelectronics "cmsis_core" repót a git-en. Viszont itt nyoma nincs az ilyen nevesített fájloknak amit idéztem, olyat látok benne mint az stm32f0xx.h -t (amit az előzőleg idézett blinky repo is használ).

Két, jónak mondható bare metal programming guide-t is találtam, az egyik a git "cpq" nevével illetve a másik "független" a Vivonomicon sorozata.
Először a cpq verzióval indultam el (persze ott sincs pont az F051 mcu) de még nem sikerült működésre bírni vele a kártyámat.
Találtam egy igen régi csomagot is egy bizonyos "Frank Duignan" blogja - több stm gyártmányú arm-al is foglalkozik, viszont igen régi, és persze nincs ott sem szó az F051-ről.

Hogy lehet összerakni, egy használható fejlesztői CMSIS fájl készletet az STM32F051 -hez?