Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Wayland (BETA) #3

Merged
merged 2 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions .github/workflows/build-native.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ jobs:
fail-fast: false
matrix:
include:
- os: ubuntu-20.04
name: "Ubuntu 20.04 x64"
- os: ubuntu-22.04
name: "Ubuntu 22.04 x64"

- os: windows-2022
name: "Windows Server 2022 x64"
Expand All @@ -22,7 +22,7 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: '(Windows) Setup MSYS2'
if: runner.os == 'Windows'
Expand All @@ -44,12 +44,18 @@ jobs:
if: runner.os == 'macOS'
run: ./build_macOS.sh

- name: '(Linux) Build CocoaInput-lib'
- name: '(Linux X11) Build CocoaInput-lib'
if: runner.os == 'Linux'
run: ./build_X11.sh

- name: '(Linux Wayland) Build caramelChat-lib'
if: runner.os == 'Linux'
run: |
sudo apt-get install -y libwayland-dev wayland-protocols
./build_Wayland.sh

- name: Upload Library
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: CocoaInput-lib_${{ runner.os }}
path: build/
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@ dkms.conf

# macOS File
.DS_Store

# IDE File
.vscode/
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ Native libraries to support CJK _(Chinese, Japanese, Korean)_ IME in Java applic

2. Compile native library<br>
(Run one of below scripts which is your platform.)
- `build_macOS.sh` - For macOS
- `build_Windows.sh` - For Windows
- `build_X11.sh` - For Linux(X11)

- CocoaInput-lib
- `build_macOS.sh` - For macOS
- `build_Windows.sh` - For Windows
- `build_X11.sh` - For Linux(X11)
- caramelChat Library
- **[BETA]** `build_Wayland.sh` - For Linux(Wayland)

## License
Use the license of the original project.
Expand Down
5 changes: 5 additions & 0 deletions build_Wayland.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
echo "Build caramelChat Library for Wayland"
mkdir -p build
cd src/wayland
make && make clean && make install
40 changes: 40 additions & 0 deletions src/wayland/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
CC = gcc
CFLAGS = -Wall -fPIC
FFLAGS =
OBJS = libcaramelchatwl.o logger.o text-input-unstable-v3-protocol.o
LIBS = -lwayland-client
TARGET = libcaramelchatwl.so
DISTDIR = ../../build/
RM = rm
CP = cp

# Dependencies
WAYLAND_PROTOCOLS_DIR = $(shell pkg-config wayland-protocols --variable=pkgdatadir)
WAYLAND_SCANNER = $(shell pkg-config --variable=wayland_scanner wayland-scanner)
GLFW = $(shell pkg-config --cflags glfw3 glu)
TEXT_INPUT_V3_PROTOCOL = $(WAYLAND_PROTOCOLS_DIR)/unstable/text-input/text-input-unstable-v3.xml

HEADERS=text-input-unstable-v3-client-protocol.h text-input-unstable-v3-protocol.c


all: $(TARGET)

install: $(TARGET)
$(CP) -f $(TARGET) $(DISTDIR)

$(TARGET): $(HEADERS) $(OBJS)
$(CC) $(OBJS) $(CFLAGS) $(FFLAGS) $(LIBS) -shared -o $@

.c.o:
$(CC) $(CFLAGS) $(LIBS) -c $<

clean:
$(RM) -f *.o $(HEADERS)
$(RM) -f $(TARGET) $(OBJS)

# Dependencies
text-input-unstable-v3-client-protocol.h:
$(WAYLAND_SCANNER) client-header $(TEXT_INPUT_V3_PROTOCOL) text-input-unstable-v3-client-protocol.h

text-input-unstable-v3-protocol.c:
$(WAYLAND_SCANNER) public-code $(TEXT_INPUT_V3_PROTOCOL) text-input-unstable-v3-protocol.c
199 changes: 199 additions & 0 deletions src/wayland/libcaramelchatwl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#include <string.h>
#include <stdlib.h>
#include <wchar.h>

#include "libcaramelchatwl.h"

struct wl_seat* wlSeat = NULL;
struct zwp_text_input_manager_v3* textInputManager = NULL;
struct zwp_text_input_v3* textInput = NULL;

void (*javaPreedit)(wchar_t*);
void (*javaPreeditNull)();
void (*javaDone)(wchar_t*);
bool (*javaRect)(float*);

void setCallback(void (*callPreedit)(wchar_t*), void (*callPreeditNull), void (*callDone)(wchar_t*), bool (*callRect)(float*)) {
javaPreedit = callPreedit;
javaPreeditNull = callPreeditNull;
javaDone = callDone;
javaRect = callRect;
}

wchar_t* convert(const char* text) {
size_t len = (strlen(text) + 1);

wchar_t* wideStr = (wchar_t*) malloc(len * sizeof(wchar_t));
if (wideStr == NULL) {
CIError("Memory allocation failed.");
return NULL;
}

if (mbstowcs(wideStr, text, len) == -1) {
CIError("Conversion failed.");
free(wideStr);
return NULL;
}

return wideStr;
}

// =================================== (Registry)

int mathMin(int a, int b) {
return (a < b) ? a : b;
}

static void _registryHandleGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
CIDebug("Wayland Registery Listener (Add): %s", interface);
if (strcmp(interface, "wl_seat") == 0) {
wlSeat = wl_registry_bind(registry, name, &wl_seat_interface, mathMin(4, version));
} else if (strcmp(interface, "zwp_text_input_manager_v3") == 0) {
textInputManager = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1);
}
}

static void _registryHandleGlobalRemove(void* data, struct wl_registry* registry, uint32_t name) {
if (wlSeat) {
wl_seat_destroy(wlSeat);
}

if (textInput) {
zwp_text_input_v3_destroy(textInput);
}
if (textInputManager) {
zwp_text_input_manager_v3_destroy(textInputManager);
}
}

static const struct wl_registry_listener _registryListener = {
_registryHandleGlobal,
_registryHandleGlobalRemove
};

// =================================== (IME)

bool enableIme = false;

static void _updateRect() {
float* rect = malloc(sizeof(float) * 4);
if (javaRect(rect)) {
free(rect);
return;
}

zwp_text_input_v3_set_cursor_rectangle(textInput, 0, (int32_t) rect[1], (int32_t) rect[0], 0);
free(rect);
}

static void _textInputEnter(void* data, struct zwp_text_input_v3* textInput, struct wl_surface* surface) {
CIDebug("IME Enter");
if (enableIme) {
zwp_text_input_v3_enable(textInput);
_updateRect();
zwp_text_input_v3_commit(textInput);
}
}

static void _textInputLeave(void* data, struct zwp_text_input_v3* textInput, struct wl_surface* surface) {
zwp_text_input_v3_disable(textInput);
zwp_text_input_v3_commit(textInput);
}

static void _textInputPreeditString(void* data, struct zwp_text_input_v3* textInput, const char* text, int32_t cursorBegin, int32_t cursorEnd) {
CIDebug("IME Preedit: \"%s\"", text);

if (text == NULL) {
javaPreeditNull();
return;
}

wchar_t* converted = convert(text);
if (converted != NULL) {
javaPreedit(converted);
free(converted);
}
}

static void _textInputCommitString(void* data, struct zwp_text_input_v3* textInput, const char* text) {
if (text == NULL) {
return;
}

CIDebug("IME CommitString: \"%s\"", text);

wchar_t* converted = convert(text);
if (converted != NULL) {
javaDone(converted);
free(converted);
}

// Update Rect
_updateRect();
zwp_text_input_v3_commit(textInput);
}

static void _textInputDeleteSurroundingText(void* data, struct zwp_text_input_v3* textInput, uint32_t beforeLength, uint32_t afterLength) {
CIDebug("IME Delete: %d %d", beforeLength, afterLength);
}

static void _textInputDone(void* data, struct zwp_text_input_v3* textInput, uint32_t serial) {
CIDebug("IME Done");
}

static const struct zwp_text_input_v3_listener _textInputListener = {
_textInputEnter,
_textInputLeave,
_textInputPreeditString,
_textInputCommitString,
_textInputDeleteSurroundingText,
_textInputDone
};

// ===================================

void initialize(
struct wl_display* display,
void (*callPreedit)(wchar_t*),
void (*callPreeditNull),
void (*callDone)(wchar_t*),
bool (*callRect)(float*),
LogFunction log,
LogFunction error,
LogFunction debug
) {
initLogPointer(log, error, debug); // TODO Rewrite Logger

struct wl_registry* _registry = wl_display_get_registry(display);
wl_registry_add_listener(_registry, &_registryListener, NULL);
wl_display_dispatch(display);

if (!wlSeat) {
CIError("Critical Error!!!");
return;
}

if (!textInputManager) {
CIError("This system is using an unsupported Wayland...");
return;
}

setCallback(callPreedit, callPreeditNull, callDone, callRect);
textInput = zwp_text_input_manager_v3_get_text_input(textInputManager, wlSeat);
zwp_text_input_v3_add_listener(textInput, &_textInputListener, display);
}

void setFocus(bool flag) {
// Change state
if (flag) {
enableIme = true;
zwp_text_input_v3_enable(textInput);
_updateRect();
} else {
enableIme = false;
zwp_text_input_v3_disable(textInput);
}

// Commit
zwp_text_input_v3_commit(textInput);
}
19 changes: 19 additions & 0 deletions src/wayland/libcaramelchatwl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <wayland-client-core.h>
#include <stdio.h>
#include <stdbool.h>

#include "text-input-unstable-v3-client-protocol.h"
#include "logger.h"

void initialize(
struct wl_display *display,
void (*callPreedit)(wchar_t*),
void (*callPreeditNull),
void (*callDone)(wchar_t*),
bool (*callRect)(float*),
LogFunction log,
LogFunction error,
LogFunction debug
);

void setFocus(bool flag);
47 changes: 47 additions & 0 deletions src/wayland/logger.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#define _GNU_SOURCE

#include "logger.h"
#include <stdio.h>
#include <stdlib.h>

struct {
LogFunction log;
LogFunction error;
LogFunction debug;
} LogPointer;

void CILog(const char *format, ...) {
char *msg;
va_list args;
va_start(args, format);
vasprintf(&msg, format, args);
LogPointer.log(msg);
free(msg);
va_end(args);
}

void CIError(const char *format, ...) {
char *msg;
va_list args;
va_start(args, format);
vasprintf(&msg, format, args);
LogPointer.error(msg);
free(msg);
va_end(args);
}

void CIDebug(const char *format, ...) {
char *msg;
va_list args;
va_start(args, format);
vasprintf(&msg, format, args);
LogPointer.debug(msg);
free(msg);
va_end(args);
}

void initLogPointer(LogFunction log, LogFunction error, LogFunction debug) {
LogPointer.log = log;
LogPointer.error = error;
LogPointer.debug = debug;
}
9 changes: 9 additions & 0 deletions src/wayland/logger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <stdarg.h>

typedef void (*LogFunction)(const char *);

void initLogPointer(LogFunction log, LogFunction error, LogFunction debug);

void CILog(const char *msg, ...);
void CIError(const char *msg, ...);
void CIDebug(const char *msg, ...);