2025, Oct 03 09:33

uinput वर्चुअल कीबोर्ड में Unicode रीमैप: सही capabilities जोड़ें

Linux evdev/uinput में वर्चुअल कीबोर्ड से Unicode इनपुट (Ctrl+Shift+U + hex) पर u0985 दिखे तो कारण capabilities हैं: KEY_LEFTCTRL और LEFTSHIFT जोड़ें.

evdev और uinput के साथ वर्चुअल कीबोर्ड के जरिए किसी भौतिक कुंजी को किसी अन्य यूनिकोड अक्षर पर रीमैप करना ऊपर-ऊपर आसान लगता है—जब तक कि टेक्स्ट फ़ील्ड उस कोड पॉइंट के अक्षर की जगह ज़िद करके “u0985” जैसा शाब्दिक पाठ न दिखाए। अगर आप Ctrl+Shift+U का अनुकरण करते हैं और फिर हेक्स अंक टाइप करते हैं, लेकिन लक्ष्य ऐप तक सिर्फ साधारण अक्षर और अंक ही पहुँचते हैं, तो दिक्कत आमतौर पर अनुक्रम में नहीं होती।

समस्या का विवरण

लक्ष्य यह है कि वास्तविक कुंजी दबाकर उसे इंटरसेप्ट किया जाए और वर्चुअल कीबोर्ड से Unicode इनपुट अनुक्रम Ctrl+Shift+U 0985 Enter इंजेक्ट कराया जाए, ताकि फोकस वाली ऐप को “u0985” जैसे कच्चे अक्षरों की बजाय वास्तविक अक्षर मिले। देखा गया व्यवहार: फोकस वाले एलिमेंट में इंजेक्ट किया गया अनुक्रम “u0985” के रूप में दिखाई देता है, संबंधित यूनिकोड अक्षर के रूप में नहीं।

समस्या दिखाने वाला पुनरुत्पादनीय कोड

यह स्निपेट एक भौतिक इनपुट डिवाइस से पढ़ता है, उसे ग्रैब करता है, और वर्चुअल uinput डिवाइस से Ctrl+Shift+U, फिर 0, 9, 8, 5 और Enter भेजता है। वर्चुअल डिवाइस अपनी क्षमताओं में सीमित कुंजियाँ घोषित करता है—और यहीं एक सूक्ष्म गलती समस्या बनती है।

import evdev
from evdev import UInput, ecodes as codes
import time
# भौतिक कीबोर्ड जैसे डिवाइस से बाइंड करें
src_dev = evdev.InputDevice('/dev/input/event2')
src_dev.grab()
# अधूरी क्षमताओं वाला वर्चुअल डिवाइस (समस्या)
virt_caps = {codes.EV_KEY: [
    codes.KEY_A,
    codes.KEY_B,
    codes.KEY_G,
    codes.KEY_U,
    codes.KEY_0,
    codes.KEY_9,
    codes.KEY_8,
    codes.KEY_5,
    codes.KEY_ENTER,
]}
vkb = UInput(virt_caps, name='virtual_kbd_demo')
for evt in src_dev.read_loop():
    if evt.type == codes.EV_KEY:
        key_info = evdev.categorize(evt)
        if key_info.keystate == evdev.KeyEvent.key_down:
            # यूनिकोड इनपुट मोड शुरू करें
            vkb.write(evt.type, codes.KEY_LEFTCTRL, 1)
            vkb.write(evt.type, codes.KEY_LEFTSHIFT, 1)
            vkb.write(evt.type, codes.KEY_U, 1)
            vkb.write(evt.type, codes.KEY_U, 0)
            vkb.write(evt.type, codes.KEY_LEFTSHIFT, 0)
            vkb.write(evt.type, codes.KEY_LEFTCTRL, 0)
            vkb.syn()
            time.sleep(0.1)
            # हेक्स अंकों को भेजें
            vkb.write(evt.type, codes.KEY_0, 1)
            vkb.write(evt.type, codes.KEY_0, 0)
            vkb.write(evt.type, codes.KEY_9, 1)
            vkb.write(evt.type, codes.KEY_9, 0)
            vkb.write(evt.type, codes.KEY_8, 1)
            vkb.write(evt.type, codes.KEY_8, 0)
            vkb.write(evt.type, codes.KEY_5, 1)
            vkb.write(evt.type, codes.KEY_5, 0)
            vkb.syn()
            time.sleep(0.1)
            # Enter के साथ पुष्टि करें
            vkb.write(evt.type, codes.KEY_ENTER, 1)
            vkb.write(evt.type, codes.KEY_ENTER, 0)
            vkb.syn()
        elif key_info.keystate == evdev.KeyEvent.key_up:
            pass
    else:
        # समय/प्रवाह को ठीक रखने के लिए गैर-कुंजी इवेंट आगे भेजें
        vkb.write(evt.type, evt.code, evt.value)
# क्लीनअप
src_dev.ungrab()
vkb.close()

असल में क्या हो रहा है

इंजेक्ट किया गया अनुक्रम सही है, लेकिन वर्चुअल डिवाइस यह घोषित नहीं करता कि वह जिन मॉडिफ़ायर कुंजियों को भेज रहा है, उन्हें सपोर्ट करता है। जब uinput डिवाइस को उसकी क्षमताओं में KEY_LEFTCTRL और KEY_LEFTSHIFT के बिना बनाया जाता है, तो उस डिवाइस से भेजे गए ये की-प्रेस स्वीकार ही नहीं होते। नतीजतन, सिस्टम सिर्फ “u” और अंक ही देखता है—इसीलिए फोकस वाला एलिमेंट यूनिकोड इनपुट मोड में जाने और लक्ष्य अक्षर कंपोज़ करने के बजाय “u0985” जैसा शाब्दिक पाठ प्राप्त करता है।

समाधान

वर्चुअल डिवाइस जिन-जिन कुंजियों को भेजेगा, उन्हें पहले से घोषित करें। इस मामले में EV_KEY क्षमता सेट में KEY_LEFTCTRL और KEY_LEFTSHIFT जोड़ें। यह परिवर्तन होते ही वही इंजेक्शन लॉजिक अपेक्षित यूनिकोड अक्षर देता है।

import evdev
from evdev import UInput, ecodes as codes
import time
# भौतिक कीबोर्ड जैसे डिवाइस से बाइंड करें
src_dev = evdev.InputDevice('/dev/input/event2')
src_dev.grab()
# अनुक्रम के लिए पूरी क्षमताओं वाला वर्चुअल डिवाइस
virt_caps = {codes.EV_KEY: [
    codes.KEY_LEFTCTRL,   # जोड़ा गया
    codes.KEY_LEFTSHIFT,  # जोड़ा गया
    codes.KEY_U,
    codes.KEY_0,
    codes.KEY_9,
    codes.KEY_8,
    codes.KEY_5,
    codes.KEY_ENTER,
]}
vkb = UInput(virt_caps, name='virtual_kbd_demo')
for evt in src_dev.read_loop():
    if evt.type == codes.EV_KEY:
        key_info = evdev.categorize(evt)
        if key_info.keystate == evdev.KeyEvent.key_down:
            # यूनिकोड इनपुट मोड शुरू करें
            vkb.write(evt.type, codes.KEY_LEFTCTRL, 1)
            vkb.write(evt.type, codes.KEY_LEFTSHIFT, 1)
            vkb.write(evt.type, codes.KEY_U, 1)
            vkb.write(evt.type, codes.KEY_U, 0)
            vkb.write(evt.type, codes.KEY_LEFTSHIFT, 0)
            vkb.write(evt.type, codes.KEY_LEFTCTRL, 0)
            vkb.syn()
            time.sleep(0.1)
            # हेक्स अंकों को भेजें
            vkb.write(evt.type, codes.KEY_0, 1)
            vkb.write(evt.type, codes.KEY_0, 0)
            vkb.write(evt.type, codes.KEY_9, 1)
            vkb.write(evt.type, codes.KEY_9, 0)
            vkb.write(evt.type, codes.KEY_8, 1)
            vkb.write(evt.type, codes.KEY_8, 0)
            vkb.write(evt.type, codes.KEY_5, 1)
            vkb.write(evt.type, codes.KEY_5, 0)
            vkb.syn()
            time.sleep(0.1)
            # Enter के साथ पुष्टि करें
            vkb.write(evt.type, codes.KEY_ENTER, 1)
            vkb.write(evt.type, codes.KEY_ENTER, 0)
            vkb.syn()
        elif key_info.keystate == evdev.KeyEvent.key_up:
            pass
    else:
        # समय/प्रवाह को ठीक रखने के लिए गैर-कुंजी इवेंट आगे भेजें
        vkb.write(evt.type, evt.code, evt.value)
# क्लीनअप
src_dev.ungrab()
vkb.close()

यह क्यों महत्वपूर्ण है

uinput के साथ वर्चुअल कीबोर्ड या रिमैपर बनाते समय क्षमता सेट एक तरह का अनुबंध होता है। इनपुट स्टैक इसी पर निर्भर करता है कि कोई डिवाइस कौन-से इवेंट वैध रूप से उत्पन्न कर सकता है। यदि कोई कुंजी घोषित नहीं है, तो उससे जुड़े इवेंट को ऊपरी लेयरें अनदेखा कर सकती हैं या गलत तरीके से संभाल सकती हैं—और फिर इच्छित अक्षर की जगह “u0985” जैसा उलझाने वाला आउटपुट मिलता है। यह बारीकी आसानी से छूट जाती है, क्योंकि अनुक्रम का बाकी सब कुछ सही लगता है और डिबगर भी सही कॉल्स दिखाते हैं।

मुख्य बातें

किसी वर्चुअल डिवाइस से कोई भी कुंजी भेजनी हो, तो उसे पहले ही EV_KEY क्षमता सूची में जोड़ दें—खासकर Ctrl और Shift जैसे मॉडिफ़ायर, जो Unicode इनपुट मोड जैसी कार्यप्रणालियों को नियंत्रित करते हैं। जब क्षमता सेट में आपकी भेजी जाने वाली हर कुंजी शामिल होती है, तो यूनिकोड कंपोज़िशन अपेक्षित रूप से काम करता है और फोकस में मौजूद ऐप को कच्चे कीस्ट्रोक्स नहीं, बल्कि वास्तविक अक्षर मिलता है।

यह लेख StackOverflow पर प्रश्न (लेखक: AL-zami) और furas के उत्तर पर आधारित है।