본문으로 건너뛰기
Elmi Tech Note Logo Elmi Tech Note Logo Elmi Tech Note

마비노기 모바일 해외 접속 & 윈도우 난민을 위한 리눅스 가이드

Elmi Tech Note Logo Elmi Tech Note Logo Elmi Tech Note

마비노기 모바일 해외 접속 & 윈도우 난민을 위한 리눅스 가이드

  • 홈
  • 🔰먼저 여기부터 클릭! [정리글] 해외에서 마비노기 모바일 VPN 접속
  • 홈
  • 🔰먼저 여기부터 클릭! [정리글] 해외에서 마비노기 모바일 VPN 접속
닫기

검색

와우_스크린샷
Cachyos(Arch Linux)게임

[Linux/KDE Wayland] Lutris + GE-Proton 월드 오브 워크래프트(WoW) 한글 입력 완벽 해결

By 엘미타
2026년 11월 09일 6 Min Read
0

https://blog.elmi.page/wp-content/uploads/2026/11/와우_스크린샷-scaled.avif

리눅스(CachyOS / Arch Linux 등) + KDE Plasma 6 (Wayland) + Fcitx5 환경에서 Lutris (GE-Proton / Wine)로 배틀넷과 월드 오브 워크래프트(WoW)를 구동할 때 겪는 고질적인 문제 3가지를 완벽하게 해결하는 가이드.

채팅창을 닫았는데도 입력이 한글 상태면 WASD 이동 등 키가 인식이 안 되는 문제

채팅 후 한/영 키를 안 바꾸고 채팅창을 닫으면, WASD를 눌렀을 때 캐릭터가 움직이지 않고 한글(ㅈ, ㅁ, ㄴ, ㅇ)이 조합되는 창이 뜨면서 와우에서 인식해야할 키가 인식이 안되는 현상 해결

채팅 입력 시 거슬리는 떠다니는 사전편집(Preedit) 팝업창 제거
한글 입력 시 화면에 별도 팝업 박스가 뜨지 않고, 윈도우 환경처럼 게임 채팅창 커서 위치에서 바로 글자가 조합되도록 설정
앱 메뉴에서 아이콘 한 번만 누르면 배틀넷이 자동 로그인 후 와우를 바로 띄우고 스스로 종료되도록 구성

TIP

이 방식은 Wine이나 게임 실행 파일(Wow.exe, winex11.so)을 직접 바이너리 패치하지 않고,
표준 LD_PRELOAD 공유 라이브러리(libwow_ime_fix.so)로 동작하므로 Proton/Wine 버전을 업데이트해도 깨지지 않고 그대로 재사용 가능

1단계 : Fcitx5 사전편집(인라인 입력) 설정

한글 조합 시 떠다니는 팝업창을 없애고 게임 채팅창 안에 직접 글자가 조합되게 하려면 일반 설정과 XIM 부가기능 설정 두 곳을 모두 설정해야 함.

  1. 일반 설정 (응용프로그램에서 사전편집 표시 / PreeditEnabledByDefault):
    원문은 “Show preedit in application”. 조합 중인 글자(사전편집)를 별도 팝업창이 아니라 응용프로그램 입력칸 내부에 직접 표시할지 묻는 옵션.
    이 옵션을 체크(True)해야 팝업창이 뜨지 않고 인라인으로 입력 됨. (체크를 해제하면 오히려 팝업창이 표시)
  2. XIM 부가기능 설정 (스팟 스타일에서 사용 / UseOnTheSpot):
    Wine/Proton 게임은 X11의 구형 XIM(X Input Method) 프로토콜을 사용한다.
    Fcitx5의 XIM 프론트엔드는 기본값이 UseOnTheSpot=False로 되어 있어, 일반 설정을 켜 두어도 Wine 게임에서는 강제로 팝업창을 띄우게 됨.
    이걸 True로 켜야 Wine(winex11.so)의 XIMPreeditCallbacks가 활성화 됨.

설정 적용 방법 (GUI 또는 설정 파일)

  1. KDE 시스템 설정 → 입력기(Fcitx5) → 전역 옵션 클릭 → 응용프로그램에서 사전편집 표시 체크
  2. 입력기(Fcitx5) → 부가기능(Addons) 탭 → 프론트엔드 → X 입력기 프론트엔드 우측 톱니바퀴 클릭 → 스팟 스타일에서 사용(다시 시작 필요) 체크

KDE Wayland에서 Fcitx5 재시작하기

KDE 시스템 설정 → 가상 키보드에서 없음을 눌렀다가 다시 Fcitx 5를 선택해 적용으로 재시작.

https://blog.elmi.page/wp-content/uploads/2026/11/가상_키보드_없음.avif

2단계 : 채팅창 닫힘 시 WASD 한글 씹힘 방지 라이브러리 (libwow_ime_fix.so) 만들기

동작 원리

와우는 채팅창을 닫을 때 Windows API ImmAssociateContext(hwnd, NULL)를 호출해 입력 컨텍스트(HIMC)를 제거함.
하지만 기본 Wine/Proton(winex11.so)은 이 신호를 리눅스 Fcitx5(XIM)로 전달하지 않아 Fcitx5가 계속 한글을 조합하려고 키 입력을 가로채서 팝업(사전편집)을 띄움.
libwow_ime_fix.so는 win32u.so의 드라이버 테이블 등록 함수(__wine_set_user_driver)와 libX11.so.6의 XIM 함수들을 후킹하여,
HIMC == 0(채팅창 닫힘)일 때는 Fcitx5 포커스를 해제하고 키 입력을 영문 Raw Key로 즉시 통과시켜서 와우가 키 입력을 알아먹도록 함.

소스 코드 작성 (wow_ime_fix.c)

배틀넷 프리픽스 폴더(예: ~/Games/battlenet/wow_ime_fix.c)에 아래 파일을 작성.

#define _GNU_SOURCE
#include <dlfcn.h>
#include <link.h>
#include <stdint.h>
#include <string.h>
typedef void *HWND;
typedef void *HIMC;
typedef void *XIC;
typedef void *XIM;
typedef void *Display;
typedef unsigned long Window;
typedef unsigned long KeySym;
typedef int Bool;
typedef int Status;
typedef union _XEvent {
    int type;
    long pad[24];
} XEvent;
/* Function pointer types */
typedef unsigned int (*pfn_wine_set_user_driver)(void *funcs, unsigned int version);
typedef uintptr_t (*pfn_NtUserCallHwnd)(HWND hwnd, unsigned int code);
typedef HWND (*pfn_NtUserGetForegroundWindow)(void);
typedef void (*pfn_EnableIMEContext)(HWND hwnd, int enable);
typedef void (*pfn_XSetICFocus)(XIC ic);
typedef void (*pfn_XUnsetICFocus)(XIC ic);
typedef Bool (*pfn_XFilterEvent)(XEvent *event, Window w);
typedef int (*pfn_XmbLookupString)(XIC ic, XEvent *event, char *buffer, int bytes, KeySym *keysym, Status *status);
typedef int (*pfn_XLookupString)(XEvent *event, char *buffer, int bytes, KeySym *keysym, void *compose);
typedef char *(*pfn_XmbResetIC)(XIC ic);
typedef int (*pfn_XFree)(void *data);
typedef XIM (*pfn_XIMOfIC)(XIC ic);
typedef Display *(*pfn_XDisplayOfIM)(XIM im);
typedef int (*pfn_XFlush)(Display *display);
static void *g_win32u_handle = NULL;
static void *g_x11_handle = NULL;
static pfn_wine_set_user_driver real_wine_set_user_driver = NULL;
static pfn_NtUserCallHwnd real_NtUserCallHwnd = NULL;
static pfn_NtUserGetForegroundWindow real_NtUserGetForegroundWindow = NULL;
static pfn_EnableIMEContext orig_EnableIMEContext = NULL;
static pfn_XSetICFocus real_XSetICFocus = NULL;
static pfn_XUnsetICFocus real_XUnsetICFocus = NULL;
static pfn_XFilterEvent real_XFilterEvent = NULL;
static pfn_XmbLookupString real_XmbLookupString = NULL;
static pfn_XLookupString real_XLookupString = NULL;
static pfn_XmbResetIC real_XmbResetIC = NULL;
static pfn_XFree real_XFree = NULL;
static pfn_XIMOfIC real_XIMOfIC = NULL;
static pfn_XDisplayOfIM real_XDisplayOfIM = NULL;
static pfn_XFlush real_XFlush = NULL;
static XIC g_last_xic = NULL;
static HWND g_tracked_hwnd = NULL;
#define NTUSER_CALL_HWND_GET_WINDOW_INPUT_CONTEXT 7
#define USER_DRIVER_FUNCS_SIZE 0x4d8
#define OFFSET_ENABLE_IME_CONTEXT 0x338
/* Status values for XmbLookupString */
#define XLookupNone   1
#define XLookupChars  2
#define XLookupKeySym 3
#define XLookupBoth   4
struct phdr_search_ctx {
    const char *suffix;
    void *handle;
};
static int phdr_callback(struct dl_phdr_info *info, size_t size, void *data)
{
    (void)size;
    struct phdr_search_ctx *ctx = (struct phdr_search_ctx *)data;
    if (!info->dlpi_name || !info->dlpi_name[0])
        return 0;
    size_t name_len = strlen(info->dlpi_name);
    size_t suffix_len = strlen(ctx->suffix);
    if (name_len >= suffix_len &&
        strcmp(info->dlpi_name + name_len - suffix_len, ctx->suffix) == 0) {
        void *h = dlopen(info->dlpi_name, RTLD_NOLOAD | RTLD_LAZY);
        if (h) {
            ctx->handle = h;
            return 1;
        }
    }
    return 0;
}
static void *find_loaded_module(const char *suffix)
{
    struct phdr_search_ctx ctx = { suffix, NULL };
    dl_iterate_phdr(phdr_callback, &ctx);
    return ctx.handle;
}
static void resolve_win32u_symbols(void)
{
    if (!g_win32u_handle)
        g_win32u_handle = find_loaded_module("/win32u.so");
    if (!g_win32u_handle)
        return;
    if (!real_wine_set_user_driver)
        real_wine_set_user_driver = (pfn_wine_set_user_driver)dlsym(g_win32u_handle, "__wine_set_user_driver");
    if (!real_NtUserCallHwnd)
        real_NtUserCallHwnd = (pfn_NtUserCallHwnd)dlsym(g_win32u_handle, "NtUserCallHwnd");
    if (!real_NtUserGetForegroundWindow)
        real_NtUserGetForegroundWindow = (pfn_NtUserGetForegroundWindow)dlsym(g_win32u_handle, "NtUserGetForegroundWindow");
}
static void resolve_x11_symbols(void)
{
    if (!g_x11_handle)
        g_x11_handle = find_loaded_module("/libX11.so.6");
    if (!g_x11_handle)
        g_x11_handle = dlopen("libX11.so.6", RTLD_NOLOAD | RTLD_LAZY);
    if (!g_x11_handle)
        g_x11_handle = dlopen("libX11.so.6", RTLD_LAZY);
    if (!g_x11_handle)
        return;
    if (!real_XFilterEvent)
        real_XFilterEvent = (pfn_XFilterEvent)dlsym(g_x11_handle, "XFilterEvent");
    if (!real_XSetICFocus)
        real_XSetICFocus = (pfn_XSetICFocus)dlsym(g_x11_handle, "XSetICFocus");
    if (!real_XUnsetICFocus)
        real_XUnsetICFocus = (pfn_XUnsetICFocus)dlsym(g_x11_handle, "XUnsetICFocus");
    if (!real_XmbLookupString)
        real_XmbLookupString = (pfn_XmbLookupString)dlsym(g_x11_handle, "XmbLookupString");
    if (!real_XLookupString)
        real_XLookupString = (pfn_XLookupString)dlsym(g_x11_handle, "XLookupString");
    if (!real_XmbResetIC)
        real_XmbResetIC = (pfn_XmbResetIC)dlsym(g_x11_handle, "XmbResetIC");
    if (!real_XFree)
        real_XFree = (pfn_XFree)dlsym(g_x11_handle, "XFree");
    if (!real_XIMOfIC)
        real_XIMOfIC = (pfn_XIMOfIC)dlsym(g_x11_handle, "XIMOfIC");
    if (!real_XDisplayOfIM)
        real_XDisplayOfIM = (pfn_XDisplayOfIM)dlsym(g_x11_handle, "XDisplayOfIM");
    if (!real_XFlush)
        real_XFlush = (pfn_XFlush)dlsym(g_x11_handle, "XFlush");
}
static void resolve_symbols(void)
{
    resolve_win32u_symbols();
    resolve_x11_symbols();
}
static int is_ime_enabled(void)
{
    resolve_symbols();
    if (!real_NtUserCallHwnd)
        return 1;
    HWND hwnd = NULL;
    if (real_NtUserGetForegroundWindow)
        hwnd = real_NtUserGetForegroundWindow();
    if (!hwnd)
        hwnd = g_tracked_hwnd;
    if (!hwnd)
        return 1;
    uintptr_t imc = real_NtUserCallHwnd(hwnd, NTUSER_CALL_HWND_GET_WINDOW_INPUT_CONTEXT);
    return (imc != 0);
}
static void flush_xic_display(XIC ic)
{
    if (!ic || !real_XIMOfIC || !real_XDisplayOfIM || !real_XFlush)
        return;
    XIM im = real_XIMOfIC(ic);
    if (!im)
        return;
    Display *dpy = real_XDisplayOfIM(im);
    if (dpy)
        real_XFlush(dpy);
}
static void hooked_EnableIMEContext(HWND hwnd, int enable)
{
    resolve_symbols();
    if (hwnd)
        g_tracked_hwnd = hwnd;
    if (orig_EnableIMEContext)
        orig_EnableIMEContext(hwnd, enable);
    XIC ic = g_last_xic;
    if (!ic)
        return;
    if (enable) {
        if (real_XSetICFocus)
            real_XSetICFocus(ic);
    } else {
        if (real_XmbResetIC) {
            char *leftover = real_XmbResetIC(ic);
            if (leftover && real_XFree)
                real_XFree(leftover);
        }
        if (real_XUnsetICFocus)
            real_XUnsetICFocus(ic);
    }
    flush_xic_display(ic);
}
unsigned int __wine_set_user_driver(void *funcs, unsigned int version)
{
    resolve_symbols();
    if (!real_wine_set_user_driver)
        real_wine_set_user_driver = (pfn_wine_set_user_driver)dlsym(RTLD_NEXT, "__wine_set_user_driver");
    if (!real_wine_set_user_driver)
        return 0;
    if (funcs && version == 0x6f) {
        static unsigned char patched_funcs[USER_DRIVER_FUNCS_SIZE];
        memcpy(patched_funcs, funcs, USER_DRIVER_FUNCS_SIZE);
        pfn_EnableIMEContext *slot = (pfn_EnableIMEContext *)(patched_funcs + OFFSET_ENABLE_IME_CONTEXT);
        if (*slot == NULL) {
            orig_EnableIMEContext = NULL;
            *slot = hooked_EnableIMEContext;
            return real_wine_set_user_driver(patched_funcs, version);
        }
    }
    return real_wine_set_user_driver(funcs, version);
}
void XSetICFocus(XIC ic)
{
    resolve_symbols();
    if (ic)
        g_last_xic = ic;
    if (!is_ime_enabled()) {
        if (real_XUnsetICFocus && ic)
            real_XUnsetICFocus(ic);
        return;
    }
    if (real_XSetICFocus && ic)
        real_XSetICFocus(ic);
}
void XUnsetICFocus(XIC ic)
{
    resolve_symbols();
    if (ic)
        g_last_xic = ic;
    if (real_XUnsetICFocus && ic)
        real_XUnsetICFocus(ic);
}
Bool XFilterEvent(XEvent *event, Window w)
{
    resolve_symbols();
    if (event && (event->type == 2 || event->type == 3)) { /* KeyPress or KeyRelease */
        if (!is_ime_enabled()) {
            if (g_last_xic && real_XUnsetICFocus)
                real_XUnsetICFocus(g_last_xic);
            return 0; /* Do not let XIM/Fcitx5 swallow WASD movement keys */
        }
    }
    return real_XFilterEvent ? real_XFilterEvent(event, w) : 0;
}
int XmbLookupString(XIC ic, XEvent *event, char *buffer, int bytes, KeySym *keysym, Status *status)
{
    resolve_symbols();
    if (ic)
        g_last_xic = ic;
    if (!is_ime_enabled() && real_XLookupString) {
        KeySym sym = 0;
        int len = real_XLookupString(event, buffer, bytes, &sym, NULL);
        if (keysym)
            *keysym = sym;
        if (status) {
            if (len > 0 && sym != 0)
                *status = XLookupBoth;
            else if (len > 0)
                *status = XLookupChars;
            else if (sym != 0)
                *status = XLookupKeySym;
            else
                *status = XLookupNone;
        }
        return len;
    }
    return real_XmbLookupString ? real_XmbLookupString(ic, event, buffer, bytes, keysym, status) : 0;
}

컴파일 명령어

gcc -shared -fPIC -O2 -Wall -Wextra \
  ~/Games/battlenet/wow_ime_fix.c \
  -o ~/Games/battlenet/libwow_ime_fix.so -ldl

3단계 : Lutris 게임 설정 (YAML)

Lutris YAML 예시 (~/.local/share/lutris/games/battlenet-*.yml):

game:
  ######## 생략 ########
game_slug: battlenet
name: Battle.net
slug: battlenet-standard
system:
  env:
    DXVK_HUD: compiler
    DXVK_STATE_CACHE_PATH: /home/user/Games/battlenet
    LD_PRELOAD: /home/user/Games/battlenet/libwow_ime_fix.so #여기에 본인 유저디렉토리로 시작하는 작성한 libwow_ime_fix.so 파일 경로를 적는다.
    STAGING_SHARED_MEMORY: '1'
    ######## 생략 ########
year: 1996

https://blog.elmi.page/wp-content/uploads/2026/11/와우_한글입력.avif

이렇게 와우 채팅창에 잘 입력 된다.

Tags:

리눅스 게이밍월드 오브 워크래프트:포에버
작성자

엘미타

일본에서 일하고 있는 자바 웹 어플리케이션 개발자.

Follow Me
다른 기사
와우 포에버 골드 샤이어
Previous

[CachyOS] Intel UHD 620 내장 그래픽에서 와우 포에버 구동기 (Windows vs 리눅스 성능 비교 & 비디오 카드 에러 해결)

댓글 없음! 첫 댓글을 남겨보세요.

답글 남기기 응답 취소

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

인기 글

이 카테고리에 속한 글

Cachyos(Arch Linux), 게임
  • [Linux/KDE Wayland] Lutris + GE-Proton 월드 오브 워크래프트(WoW) 한글 입력 완벽 해결 2026년 11월 09일
  • [CachyOS] Intel UHD 620 내장 그래픽에서 와우 포에버 구동기 (Windows vs 리눅스 성능 비교 & 비디오 카드 에러 해결) 2026년 11월 08일
  • [모비노기] 유니티 게임 “용량 부족(메모리 부족)” 에러의 기술적 원인과 해결법 (마비노기 모바일 / Wine GetProcessMemoryInfo 버그 분석) 2026년 10월 22일
  • [모비노기] Wayland 환경에서 Wine·Proton 게임 내 일본어(fcitx5-mozc) / 한글 입력 완벽 해결 가이드 2026년 10월 21일
  • [모비노기] 모비노기 설치를 위한 넥슨 게임 매니저(NGM) 설치 가이드 2026년 10월 20일
  • [모비노기] 마비노기 모바일 일본판(PC) 리눅스 구동 설정 가이드 2026년 10월 20일
  • CachyOS (Arch Linux) 환경 Lutris 와우(WoW) 완벽 트러블슈팅 가이드 2026년 10월 02일
  • [리눅스 데스크탑] 구글 드라이브 등 클라우드 스토리지를 드라이브로 마운트 해서 사용 2026년 05월 02일
  • 해외에서 마비노기 모바일 VPN 접속 방법 총 정리 (차단 우회, 에러 88 & L.-9 해결) 2026년 02월 21일
  • 아치리눅스, 캐시os, 엔데버os 차이 2026년 02월 07일

최신 글

  • [Linux/KDE Wayland] Lutris + GE-Proton 월드 오브 워크래프트(WoW) 한글 입력 완벽 해결
  • [CachyOS] Intel UHD 620 내장 그래픽에서 와우 포에버 구동기 (Windows vs 리눅스 성능 비교 & 비디오 카드 에러 해결)
  • [모비노기] 유니티 게임 “용량 부족(메모리 부족)” 에러의 기술적 원인과 해결법 (마비노기 모바일 / Wine GetProcessMemoryInfo 버그 분석)
  • [모비노기] Wayland 환경에서 Wine·Proton 게임 내 일본어(fcitx5-mozc) / 한글 입력 완벽 해결 가이드
  • [모비노기] 모비노기 설치를 위한 넥슨 게임 매니저(NGM) 설치 가이드

최신 댓글

  • 엘미타 - [모비노기] 버츄얼 박스 대신 hyper-v 경유해서 해외에서 접속하기
  • moon - [모비노기] 버츄얼 박스 대신 hyper-v 경유해서 해외에서 접속하기
  • 엘미타 - [PC] 마비노기 모바일 해외 접속 버츄얼 박스 feat. 미꾸라지
  • 하나 - [PC] 마비노기 모바일 해외 접속 버츄얼 박스 feat. 미꾸라지
  • 엘미타 - 해외에서 마비노기 모바일 VPN 접속 방법 총 정리 (차단 우회, 에러 88 & L.-9 해결)

카테고리

태그

0x80bd0009 fcitx5 github gui Java kodi nginx Niri Noctalia Ollama OpenWrt sciter smb Swing wireguard Xbox series x 건틀릿 공유기 루트리스 룬테라 리눅스 리눅스 게이밍 리눅스 데스크탑 리눅스 배포판 리눅스 입문 마비노기 모바일 마인크래프트 명일방주 엔드필드 모바일 게임 모비노기 사이버 펑크 2077 오즈 리라이트 우분투 데스크탑 24.04 LTS 우분투 서버 20.04 LTS 우분투 서버 22.04 LTS 우분투 서버 24.04 LTS 월드 오브 워크래프트 윈도우 11 윈도우11 인터넷 공유 칼럼 컵히어로즈 포로덱 프로톤 해외

Copyright 2026 — Elmi Tech Note. All rights reserved. Blogsy WordPress Theme