FFI (Foreign Function Interface)

永井 忠一 2025.11.24


Xlib

レガシーライブラリの利用。例

C/C++
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

int main(int argc, char *argv[])
{
	Display *display = XOpenDisplay(nullptr);
	if (display == nullptr) {
		exit(1);
	}
	Window window = XCreateSimpleWindow(display,
					    RootWindow(display, 0),
					    100, 100, 320, 200, 2,
					    BlackPixel(display, 0), WhitePixel(display, 0));
	GC context = XCreateGC(display, window, 0, nullptr);

	XSelectInput(display, window, (ExposureMask
				       |((ButtonPressMask|ButtonReleaseMask)|PointerMotionMask)
				       |(KeyPressMask|KeyReleaseMask)
				       |StructureNotifyMask));
	XStoreName(display, window, "test");
	XMapWindow(display, window);

	XEvent event;
	while (true) {
		memset(&event, 0x00, sizeof(event));
		XNextEvent(display, &event);
		assert(event.type < LASTEvent);
		if (event.type == Expose) {
			XDrawLine(display, window, context, 10, 10, 100, 100);
			XDrawLine(display, window, context, 10, 100, 100, 10);
			XPoint points[] = {
				{10, 10},
				{10, 100},
				{100, 100},
				{100, 10},
				{10, 10}
			};
			XDrawLines(display, window, context, points, sizeof(points)/sizeof(points[0]), CoordModeOrigin);
			XDrawArc(display, window, context, 110, 10, 90, 90, 90*64/* 1/64 deg */, 180*64/* 1/64 deg */);
			XDrawString(display, window, context, 160, 120, "hello, X", 8);
			XFlush(display);
		}
		else if (event.type == ButtonPress || event.type == ButtonRelease) {
			printf("button(%s) = %u\n", (event.type == ButtonPress ? "press" : "release"), event.xbutton.button);
			printf("x, y = %d, %d\n", event.xbutton.x, event.xbutton.y);
		}
		else if (event.type == MotionNotify) {
			printf("x, y = %d, %d\n", event.xmotion.x, event.xmotion.y);
		}
		else if (event.type == KeyPress || event.type == KeyRelease) {
			printf("keycode(%s) = %u\n", (event.type == KeyPress ? "press" : "release"), event.xkey.keycode);
			if (event.type == KeyPress) {
				const KeySym key = XLookupKeysym(&(event.xkey), 0);
				if (key == XK_q) {
					break;
				}
				else if (key == XK_d) {
					XClearWindow(display, window);
				}
			}
		}
		else if (event.type == ConfigureNotify) {
			printf("width, height = %d, %d\n", event.xconfigure.width, event.xconfigure.height);
		}
	}

	XCloseDisplay(display), display = nullptr;
}

$ g++ -Wall hello.cpp -lX11 && ./a.out

ほかの言語からは、C のマクロを使うことができないため、ラッパーを用意

C
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#undef RootWindow /* override */
Window RootWindow(const Display *dpy, int scr)
{
	return ScreenOfDisplay(dpy, scr)->root;
}

#undef BlackPixel /* override */
unsigned long BlackPixel(const Display *dpy, int scr)
{
	return ScreenOfDisplay(dpy, scr)->black_pixel;
}

#undef WhitePixel /* override */
unsigned long WhitePixel(const Display *dpy, int scr)
{
        return ScreenOfDisplay(dpy, scr)->white_pixel;
}

$ test wrapper.c -nt wrapper.o && gcc -Wall -c wrapper.c
$ file wrapper.o
wrapper.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

D、Go、Rust

(D と Go の GC は、ともに、コンパクションをしない。Go には、goroutineスケジューラによる挙動がある)

DGoRust
extern(C) {
    struct Display; // incomplete type, opaque data type
    
    Display *XOpenDisplay(const char *display_name);

    alias XID = ulong;
    alias Window = XID;

    Window XCreateSimpleWindow(Display *display,
                               Window parent,
                               int x,
                               int y,
                               uint width,
                               uint height,
                               uint border_width,
                               ulong border,
                               ulong background);

    struct _XGC; // incomplete type
    alias GC = _XGC*; // opaque data type

    alias Drawable = XID;

    struct XGCValues; // unused

    GC XCreateGC(Display *display,
                 Drawable d,
                 ulong valuemask,
                 XGCValues *values);

    int XSelectInput(Display *display,
                     Window w,
                     long event_mask);

    int XStoreName(Display *display,
                   Window w,
                   const char *window_name);

    int XStoreName(Display *display,
                   Window w,
                   const char *window_name);

    int XMapWindow(Display *display,
                   Window w);

    struct XKeyEvent {
        ubyte[84] pad_to_keycode; // adhoc
        uint keycode;
        ubyte[4] pad; // adhoc
    }

    struct XButtonEvent {
        ubyte[64] pad_to_x; // adhoc
        int x;
        int y;
        ubyte[84 - 4 - 4 - 64] pad_to_button; // adhoc
        uint button;
        // omitted
    }

    struct XMotionEvent {
        ubyte[64] pad_to_x; // adhoc
        int x;
        int y;
        // omitted
    }

    struct XConfigureEvent {
        ubyte[56] pad_to_x; // adhoc
        int width;
        int height;
        // omitted
    }

    union XEvent {
        int type;
        // omitted
        XKeyEvent xkey;
        XButtonEvent xbutton;
        XMotionEvent xmotion;
        // omitted
        XConfigureEvent xconfigure;
        // omitted
        long[24] pad;
    }

    int XNextEvent(Display *display,
                   XEvent *event_return);

    int XDrawLine(Display *display,
                  Drawable d,
                  GC gc,
                  int x1,
                  int y1,
                  int x2,
                  int y2);

    struct XPoint {
        short x;
        short y;
    }

    int XDrawLines(Display *display,
                   Drawable d,
                   GC gc,
                   XPoint *points,
                   int npoints,
                   int mode);

    int XDrawArc(Display *display,
                 Drawable d,
                 GC gc,
                 int x,
                 int y,
                 uint width,
                 uint height,
                 int angle1,
                 int angle2);

    int XDrawString(Display *display,
                    Drawable d,
                    GC gc,
                    int x,
                    int y,
                    const char *string,
                    int length);

    int XFlush(Display *display);

    alias KeySym = XID;
 
    KeySym XLookupKeysym(XKeyEvent *key_event,
                         int index);

    int XClearWindow(Display *display,
                     Window w);

    int XCloseDisplay(Display *display);
}

enum {
    KeyPressMask = 1L<<0,
    KeyReleaseMask = 1L<<1,
    ButtonPressMask = 1L<<2,
    ButtonReleaseMask = 1L<<3,
    // omitted
    PointerMotionMask = 1L<<6,
    // omitted
    ExposureMask = 1L<<15,
    // omitted
    StructureNotifyMask = 1L<<17
    // omitted
}

enum {
    KeyPress = 2,
    KeyRelease = 3,
    ButtonPress = 4,
    ButtonRelease = 5,
    MotionNotify = 6,
    // omitted
    Expose = 12,
    // omitted
    ConfigureNotify = 22,
    // omitted
    LASTEvent = 36
}

enum {
    CoordModeOrigin = 0
    // omitted
}

enum : uint {
    XK_d = 0x0064,
    // omitted
    XK_q = 0x0071
}

extern(C) { // for macro
     Window RootWindow(Display *dpy, int scr);
     ulong BlackPixel(Display *dpy, int scr);
     ulong WhitePixel(Display *dpy, int scr);
}

import std.stdio;
import core.stdc.stdlib;
import core.stdc.string;

void main() {
    Display *display = XOpenDisplay(null);
    if (display is null) {
        exit(1);
    }
    Window window = XCreateSimpleWindow(display,
                                        RootWindow(display, 0),
                                        100, 100, 320, 200, 2,
                                        BlackPixel(display, 0), WhitePixel(display, 0));
    GC context = XCreateGC(display, window, 0, null);

    XSelectInput(display, window, (ExposureMask
                                   |((ButtonPressMask|ButtonReleaseMask)|PointerMotionMask)
                                   |(KeyPressMask|KeyReleaseMask)
                                   |StructureNotifyMask));
    XStoreName(display, window, "test");
    XMapWindow(display, window);

    XEvent event;
    while (true) {
        memset(&event, 0x00, event.sizeof); static assert(event.sizeof == XEvent.sizeof);
        XNextEvent(display, &event);
        if (event.type == Expose) {
            XDrawLine(display, window, context, 10, 10, 100, 100);
            XDrawLine(display, window, context, 10, 100, 100, 10);
            XPoint[] points = [XPoint(10, 10),
                               XPoint(10, 100),
                               XPoint(100, 100),
                               XPoint(100, 10),
                               XPoint(10, 10)];
            XDrawLines(display, window, context, points.ptr, cast(int)(points.length), CoordModeOrigin);
            XDrawArc(display, window, context, 110, 10, 90, 90, 90*64/* 1/64 deg */, 180*64/* 1/64 deg */);
            XDrawString(display, window, context, 160, 120, "hello, X", 8);
            XFlush(display);
        }
        else if (event.type == ButtonPress || event.type == ButtonRelease) {
            writeln("button(", (event.type == ButtonPress ? "press" : "release"), ") = ", event.xbutton.button);
            writeln("x, y = ", event.xbutton.x, ", ", event.xbutton.y);
        }
        else if (event.type == MotionNotify) {
            writeln("x, y = ", event.xmotion.x, ", ", event.xmotion.y);
        }
        else if (event.type == KeyPress || event.type == KeyRelease) {
            writeln("keycode(", (event.type == KeyPress ? "press" : "release"), ") = ", event.xkey.keycode);
            if (event.type == KeyPress) {
                const KeySym key = XLookupKeysym(&(event.xkey), 0);
                if (key == XK_q) {
                    break;
                }
                else if (key == XK_d) {
                    XClearWindow(display, window);
                }
            }
        }
        else if (event.type == ConfigureNotify) {
            writeln("width, height = ", event.xconfigure.width, ", ", event.xconfigure.height);
        }
    }

    XCloseDisplay(display); display = null;
}

$ ldc2 hello.d -L./wrapper.o -L-lX11

$ gdc -Wall hello.d wrapper.o -lX11
package main

/*
#cgo LDFLAGS: ./wrapper.o -lX11

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

#ifdef RootWindow
#undef RootWindow
#endif
Window RootWindow(const Display *dpy, int scr);

#ifdef BlackPixel
#undef BlackPixel
#endif
unsigned long BlackPixel(const Display *dpy, int scr);

#ifdef WhitePixel
#undef WhitePixel
#endif
unsigned long WhitePixel(const Display *dpy, int scr);

int get_type(const XEvent *event) {
	return event ? event->type : 0;
}
*/
import "C"

import (
	"fmt"
	"sync"
	"runtime"
	"os"
	"unsafe"
)

func main() {
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		runtime.LockOSThread()
		defer runtime.UnlockOSThread()

		var display *C.Display = C.XOpenDisplay(nil)
		if display == nil {
			os.Exit(1)
		}
		var window C.Window = C.XCreateSimpleWindow(display,
			C.RootWindow(display, 0),
			100, 100, 320, 200, 2,
			C.BlackPixel(display, 0), C.WhitePixel(display, 0))
		var context C.GC = C.XCreateGC(display, window, 0, nil)

		C.XSelectInput(display, window, (C.ExposureMask|
			((C.ButtonPressMask|C.ButtonReleaseMask)|C.PointerMotionMask)|
			(C.KeyPressMask|C.KeyReleaseMask)|
			C.StructureNotifyMask))
		{
			name := C.CString("test")
			C.XStoreName(display, window, name)
			C.free(unsafe.Pointer(name))
		}
		C.XMapWindow(display, window)

		var event C.XEvent
		for {
			C.memset(unsafe.Pointer(&event), 0x00, C.sizeof_XEvent)
			C.XNextEvent(display, &event)
			// assert(event.type < LASTEvent);
			if event_type := C.get_type(&event); event_type == C.Expose {
				C.XDrawLine(display, window, context, 10, 10, 100, 100)
				C.XDrawLine(display, window, context, 10, 100, 100, 10)
				points := []C.XPoint{
					{x: 10, y: 10},
					{x: 10, y: 100},
					{x: 100, y: 100},
					{x: 100, y: 10},
					{x: 10, y: 10},
				}
				C.XDrawLines(display, window, context, &(points[0]), C.int(len(points)), C.CoordModeOrigin)
				C.XDrawArc(display, window, context, 110, 10, 90, 90, 90*64/* 1/64 deg */, 180*64/* 1/64 deg */)
				{
					string := C.CString("hello, X")
					C.XDrawString(display, window, context, 160, 120, string, 8)
					C.free(unsafe.Pointer(string))
				}
				C.XFlush(display)
			} else if event_type == C.ButtonPress || event_type == C.ButtonRelease {
				xbutton := (*C.XButtonEvent)(unsafe.Pointer(&event))
				if event_type == C.ButtonPress {
					fmt.Printf("button(press) = %d\n", xbutton.button)
				} else {
					fmt.Printf("button(release) = %d\n", xbutton.button)
				}
				fmt.Printf("x, y = %d, %d\n", xbutton.x, xbutton.y)
			} else if event_type == C.MotionNotify {
				xmotion := (*C.XMotionEvent)(unsafe.Pointer(&event))
				fmt.Printf("x, y = %d, %d\n", xmotion.x, xmotion.y)
			} else if event_type == C.KeyPress || event_type == C.KeyRelease {
				xkey := (*C.XKeyEvent)(unsafe.Pointer(&event))
				if event_type == C.KeyPress {
					fmt.Printf("keycode(press) = %d\n", xkey.keycode)
				} else {
					fmt.Printf("keycode(release) = %d\n", xkey.keycode)
				}

				if event_type == C.KeyPress {
					var key C.KeySym = C.XLookupKeysym(xkey, 0)
					if key == C.XK_q {
						break
					} else if key == C.XK_d {
						C.XClearWindow(display, window)
					}
				}
			} else if event_type == C.ConfigureNotify {
				xconfigure := (*C.XConfigureEvent)(unsafe.Pointer(&event))
				fmt.Printf("width, height = %d, %d\n", xconfigure.width, xconfigure.height)
			}
		}

		C.XCloseDisplay(display); display = nil
	}()
	wg.Wait()
}

$ go build hello.go
use std::os::raw::{c_short, c_int, c_long, c_char, c_uint, c_ulong};

type CARD32 = u32;
type XID = CARD32;

type Window = XID;
type Drawable = XID;
// omitted
type KeySym = XID;

#[repr(C)]
struct _XDisplay {
    _private: [u8; 0] // incomplete type
}
type Display = _XDisplay; // opaque data type

#[repr(C)]
struct _XGC {
    _private: [u8; 0] // incomplete type
}
type GC = *mut _XGC; // opaque data type

#[repr(C)]
struct XGCValues {
    pad: [u8; 128] // unused
}

#[repr(C)]
#[derive(Copy, Clone)]
struct XKeyEvent {
    pad_to_keycode: [u8; 84], // adhoc
    pub keycode: c_uint,
    pad: [u8; 4] // adhoc
}

#[repr(C)]
#[derive(Copy, Clone)]
struct XButtonEvent {
    pad_to_x: [u8; 64], // adhoc
    pub x: c_int,
    pub y: c_int,
    pad_to_button: [u8; 84 - 4 - 4 - 64], // adhoc
    pub button: c_uint
    // omitted
}

#[repr(C)]
#[derive(Copy, Clone)]
struct XMotionEvent {
    pad_to_x: [u8; 64], // adhoc
    pub x: c_int,
    pub y: c_int
    // omitted
}

#[repr(C)]
#[derive(Copy, Clone)]
struct XConfigureEvent {
    pad_to_x: [u8; 56], // adhoc
    pub width: c_int,
    pub height: c_int
    // omitted
}

#[repr(C)]
union XEvent {
    pub type_: c_int,
    // omitted
    pub xkey: XKeyEvent,
    pub xbutton: XButtonEvent,
    pub xmotion: XMotionEvent,
    // omitted
    pub xconfigure: XConfigureEvent,
    // omitted
    pad: [c_long; 24]
}

#[repr(C)]
struct XPoint {
    x: c_short,
    y: c_short
}

#[allow(non_upper_case_globals)] const KeyPressMask: c_long = 1<<0;
#[allow(non_upper_case_globals)] const KeyReleaseMask: c_long = 1<<1;
#[allow(non_upper_case_globals)] const ButtonPressMask: c_long = 1<<2;
#[allow(non_upper_case_globals)] const ButtonReleaseMask: c_long = 1<<3;
// omitted
#[allow(non_upper_case_globals)] const PointerMotionMask: c_long = 1<<6;
// omitted
#[allow(non_upper_case_globals)] const ExposureMask: c_long = 1<<15;
// omitted
#[allow(non_upper_case_globals)] const StructureNotifyMask: c_long = 1<<17;
// omitted

#[allow(non_upper_case_globals)] const KeyPress: c_int = 2;
#[allow(non_upper_case_globals)] const KeyRelease: c_int = 3;
#[allow(non_upper_case_globals)] const ButtonPress: c_int = 4;
#[allow(non_upper_case_globals)] const ButtonRelease: c_int = 5;
#[allow(non_upper_case_globals)] const MotionNotify: c_int = 6;
// omitted
#[allow(non_upper_case_globals)] const Expose: c_int = 12;
// omitted
#[allow(non_upper_case_globals)] const ConfigureNotify: c_int = 22;
// omitted
#[allow(non_upper_case_globals)] const LASTEvent: c_int = 36;

#[allow(non_upper_case_globals)] const CoordModeOrigin: c_int = 0;
// omitted

#[allow(non_upper_case_globals)] const XK_d: u32 = 0x0064;
// omitted
#[allow(non_upper_case_globals)] const XK_q: u32 = 0x0071;

extern "C" {
    fn XOpenDisplay(display_name: *const c_char) -> *mut Display;
    fn XCreateSimpleWindow(display: *mut Display,
                           parent: Window,
                           x: c_int,
                           y: c_int,
                           width: c_uint,
                           height: c_uint,
                           border_width: c_uint,
                           border: c_ulong,
                           background: c_ulong) -> Window;
    fn XCreateGC(display: *mut Display,
                 d: Drawable,
                 valuemask: c_ulong,
                 values: *mut XGCValues) -> GC;
    fn XSelectInput(display: *mut Display,
                    w: Window,
                    event_mask: c_long) -> c_int;
    fn XStoreName(display: *mut Display,
                  w: Window,
                  window_name: *const c_char) -> c_int;
    fn XMapWindow(display: *mut Display,
                  w: Window) -> c_int;
    fn XNextEvent(display: *mut Display,
                  event_return: *mut XEvent) -> c_int;
    fn XDrawLine(display: *mut Display,
                 d: Drawable,
                 gc: GC,
                 x1: c_int,
                 y1: c_int,
                 x2: c_int,
                 y2: c_int) -> c_int;
    fn XDrawLines(display: *mut Display,
                  d: Drawable,
                  gc: GC,
                  points: *mut XPoint,
                  npoints: c_int,
                  mode: c_int) -> c_int;
    fn XDrawArc(display: *mut Display,
                d: Drawable,
                gc: GC,
                x: c_int,
                y: c_int,
                width: c_uint,
                height: c_uint,
                angle1: c_int,
                angle2: c_int) -> c_int;
    fn XDrawString(display: *mut Display,
                   d: Drawable,
                   gc: GC,
                   x: c_int,
                   y: c_int,
                   string: *const c_char,
                   length: c_int) -> c_int;
    fn XFlush(display: *mut Display) -> c_int;
    fn XLookupKeysym(key_event: *mut XKeyEvent,
                     index: c_int) -> KeySym;
    fn XClearWindow(display: *mut Display,
                    w: Window) -> c_int;
    fn XCloseDisplay(display: *mut Display) -> c_int;
}

extern "C" { // for macro
    fn RootWindow(dpy: *mut Display, scr: c_int) -> Window;
    fn BlackPixel(dpy: *mut Display, scr: c_int) -> c_ulong;
    fn WhitePixel(dpy: *mut Display, scr: c_int) -> c_ulong;
}

use std::ptr;
use std::process;
use std::ffi::CString;
use std::mem;

fn main() {
    unsafe { // adhoc
        let display: *mut Display = XOpenDisplay(ptr::null());
        if display.is_null() {
            process::exit(1);
        }
        let window: Window = XCreateSimpleWindow(display,
                                                 RootWindow(display, 0),
                                                 100, 100, 320, 200, 2,
                                                 BlackPixel(display, 0), WhitePixel(display, 0));
        let context: GC = XCreateGC(display, window, 0, ptr::null_mut());

        XSelectInput(display, window,
                     ExposureMask
                     |ButtonPressMask|ButtonReleaseMask|PointerMotionMask
                     |KeyPressMask|KeyReleaseMask
                     |StructureNotifyMask);
        {
            let name = CString::new("test").unwrap();
            XStoreName(display, window, name.as_ptr());
        }
        XMapWindow(display, window);

        let mut event: XEvent = mem::zeroed();
        loop {
            ptr::write_bytes(&mut event as *mut XEvent as *mut u8, 0x00, mem::size_of::<XEvent>());
            XNextEvent(display, &mut event);
            assert!(event.type_ < LASTEvent);
            if event.type_ == Expose {
                XDrawLine(display, window, context, 10, 10, 100, 100);
                XDrawLine(display, window, context, 10, 100, 100, 10);
                let mut points = [
                    XPoint { x: 10, y: 10 },
                    XPoint { x: 10, y: 100 },
                    XPoint { x: 100, y: 100 },
                    XPoint { x: 100, y: 10 },
                    XPoint { x: 10, y: 10 }
                ];
                XDrawLines(display, window, context, points.as_mut_ptr(), points.len() as c_int, CoordModeOrigin);
                XDrawArc(display, window, context, 110, 10, 90, 90, 90*64/* 1/64 deg */, 180*64/* 1/64 deg */);
                {
                    let string = CString::new("hello, X").unwrap();
                    XDrawString(display, window, context, 160, 120, string.as_ptr(), 8);
                }
                XFlush(display);
            }
            else if event.type_ == ButtonPress || event.type_ == ButtonRelease {
                println!("button({}) = {}", if event.type_ == ButtonPress { "press" } else { "release" }, event.xbutton.button);
                println!("x, y = {}, {}", event.xbutton.x, event.xbutton.y);
            }
            else if event.type_ == MotionNotify {
                println!("x, y = {}, {}", event.xmotion.x, event.xmotion.y);
            }
            else if event.type_ == KeyPress || event.type_ == KeyRelease {
                println!("keycode({}) = {}", if event.type_ == KeyPress { "press" } else { "release" }, event.xkey.keycode);
                let key: KeySym = XLookupKeysym(&mut(event.xkey), 0);
                if key == XK_q {
                    break;
                }
                else if key == XK_d {
                    XClearWindow(display, window);
                }
            }
            else if event.type_ == ConfigureNotify {
                println!("width, height = {}, {}", event.xconfigure.width, event.xconfigure.height);
            }
        }

        XCloseDisplay(display);
    }
}

$ rustc hello.rs -C link-args=wrapper.o -lX11

(参照したドキュメント)

実行結果

言語処理系

インストール

Linux apt
$ sudo apt install rustc elpa-rust-mode
$ sudo apt install golang-go elpa-go-mode
$ sudo apt install ldc gdc

$ sudo apt install python3-pygments

(シンタックスハイライトは、pygmentize)


© 2025 Tadakazu Nagai