Skip to content

Instantly share code, notes, and snippets.

@kropp
Created February 7, 2019 22:54
Show Gist options
  • Save kropp/bd8628cce07c81a0c458be565bad4801 to your computer and use it in GitHub Desktop.
Save kropp/bd8628cce07c81a0c458be565bad4801 to your computer and use it in GitHub Desktop.
X11 app in Kotln/Native
package com.github.kropp
import kotlinx.cinterop.*
import x11.*
const val APPLICATION_NAME = "spotlight"
const val SIZE = 800
@ExperimentalUnsignedTypes
data class Size(val width: UInt, val height: UInt) {
companion object {
val EMPTY = Size(0u, 0u)
}
}
@ExperimentalUnsignedTypes
fun screenSize(display: CPointer<Display>): Size {
val screen = XDefaultScreenOfDisplay(display) ?: return Size.EMPTY
val screenWidth = screen.pointed.width.toUInt()
val screenHeight = screen.pointed.height.toUInt()
XFree(screen)
return Size(screenWidth, screenHeight)
}
@kotlin.ExperimentalUnsignedTypes
fun main() {
val display = XOpenDisplay(null) ?: error("Can't open display")
val screenNumber = XDefaultScreen(display)
val screen = screenSize(display)
val window = memScoped {
val visualInfo = alloc<XVisualInfo>()
XMatchVisualInfo(display, screenNumber, 32, TrueColor, visualInfo.ptr)
val attrs = alloc<XSetWindowAttributes> {
override_redirect = 1
colormap = XCreateColormap(display, XDefaultRootWindow(display), visualInfo.visual, AllocNone)
border_pixel = 0UL
background_pixel = 0xffffffUL
}.ptr
XCreateWindow(display, XRootWindow(display, screenNumber), 0, 0, screen.width, screen.height, 0, visualInfo.depth, CopyFromParent.toUInt(),
visualInfo.visual, (CWColormap or CWBorderPixel or CWBackPixel or CWOverrideRedirect).toULong(), attrs)
}
XStoreName(display, window, APPLICATION_NAME)
XMapWindow(display, window)
XRaiseWindow(display, window)
XFlush(display)
val mouse = MousePointer(display, window)
memScoped {
val hint = alloc<XClassHint> {
res_name = APPLICATION_NAME.cstr.ptr
res_class = APPLICATION_NAME.cstr.ptr
}
XSetClassHint(display, window, hint.ptr)
}
val gc = memScoped {
val values = alloc<XGCValues> { graphics_exposures = False }.ptr
XCreateGC(display, window, 0UL, values)
}
memScoped {
// Keep the window on top
val e = alloc<XEvent> {
xclient.apply {
type = ClientMessage
message_type = XInternAtom(display, "_NET_WM_STATE", False)
this.display = display
this.window = window
format = 32
serial = 0UL
data.l[0] = 1
data.l[1] = XInternAtom(display, "_NET_WM_STATE_STAYS_ON_TOP", False).toLong()
}
}
XSendEvent(display, XRootWindow(display, screenNumber), False, SubstructureRedirectMask, e.ptr)
XSetForeground(display, gc, 0x55000000UL)
XFillRectangle(display, window, gc, 0, 0, screen.width, screen.height)
XFlush(display)
XSelectInput(display, window, PointerMotionMask or ButtonPressMask or ButtonReleaseMask or KeyPressMask or KeyReleaseMask)
val event = alloc<XEvent>()
mouse.hide()
while (true) {
XNextEvent(display, event.ptr)
XSetForeground(display, gc, 0xdd000000UL)
XFillRectangle(display, window, gc, 0, 0, screen.width, screen.height)
if (event.type == MotionNotify) {
XSetForeground(display, gc, 0x00000000UL)
XFillArc(display, window, gc, event.xmotion.x - SIZE / 2, event.xmotion.y - SIZE / 2, SIZE.toUInt(), SIZE.toUInt(), 0, 360 * 64)
XFlush(display)
}
}
}
}
@kotlin.ExperimentalUnsignedTypes
class MousePointer(private val display: CPointer<Display>, private val window: Window) {
fun hide() = memScoped {
val bitmap = XCreateBitmapFromData(display, window, ByteArray(8) { 0 }.toCValues(), 8, 8)
val black = alloc<XColor> {
red = 0U
green = 0U
blue = 0U
}
val cursor = XCreatePixmapCursor(display, bitmap, bitmap, black.ptr, black.ptr, 0, 0)
XDefineCursor(display, window, cursor)
XFreeCursor(display, cursor)
XFreePixmap(display, bitmap)
}
fun show() {
XUndefineCursor(display, window)
}
}
package = x11
headers = X11/Xlib.h X11/Xutil.h X11/Xatom.h
headerFilter = X11/*
compilerOpts = -I/usr/include/ -I/usr/include/x86_64-linux-gnu
linkerOpts = -lX11 -L/usr/lib/x86_64-linux-gnu/
noStringConversion = XCreateBitmapFromData
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment