2HQD2V46JQIUYA5AZE2VXEUG3C4VTZXB5XUEZXVV5XROZHZ2PNYQC {.deadCodeElim: on.}when defined linux:import ../platforms/unix/x11/[x, xlib, xcb, xkb, xkblib, xkbCommon, keysyms]#, platforms/unix/x11/[xkblib, xlib, xkb, ]from os import getEnvtypeKey* {.pure.} = enumA = 0BCDEFGHIJKLMNOPQRSTUVWYXZK1K2K3K4K5K6K7K8K9K0SpaceTabUpDownLeftRightF1F2F3F4F5F6F7F8F9F10F11F12LSuperRSuperLCtrlRCtrlLShiftRShiftLAltRAltMinusEqualsBackSpaceLBracketRBracketEnterSemiColonAposTildeBSlashCommaPeriodFSlashCapsEscNPMultNumLockScrlNP7NP8NP9NPMinNP4NP5NP6NPAddNP1NP2NP3NP0NPDeciNPEnterNPDivPrntHomePgUpEndPgDownInsDelPauseMenuNPEqualsKeyState* {.pure.} = enum Up Down Held ScrolledUp ScrolledDownKey_Action* {.pure.} = enum Release PressAKeyObj = object of RootObjkey*: Keystate*: KeyStatewhen defined linux:keySym*: KeySym#scanCode: int#name: stringA_Key* = ref object of AKeyObjvarkeys*: array[105, A_Key] = [ A_Key( key: A, state: KeyState.Up), A_Key( key: B, state: KeyState.Up), A_Key( key: C, state: KeyState.Up), A_Key( key: D, state: KeyState.Up), A_Key( key: E, state: KeyState.Up), A_Key( key: Key.F, state: KeyState.Up), A_Key( key: G, state: KeyState.Up), A_Key( key: H, state: KeyState.Up), A_Key( key: I, state: KeyState.Up), A_Key( key: J, state: KeyState.Up), A_Key( key: K, state: KeyState.Up), A_Key( key: L, state: KeyState.Up), A_Key( key: M, state: KeyState.Up), A_Key( key: N, state: KeyState.Up), A_Key( key: O, state: KeyState.Up), A_Key( key: P, state: KeyState.Up), A_Key( key: Q, state: KeyState.Up), A_Key( key: R, state: KeyState.Up), A_Key( key: S, state: KeyState.Up), A_Key( key: T, state: KeyState.Up), A_Key( key: U, state: KeyState.Up), A_Key( key: V, state: KeyState.Up), A_Key( key: W, state: KeyState.Up), A_Key( key: Y, state: KeyState.Up), A_Key( key: X, state: KeyState.Up), A_Key( key: Z, state: KeyState.Up), A_Key( key: K1, state: KeyState.Up), A_Key( key: K2, state: KeyState.Up), A_Key( key: K3, state: KeyState.Up), A_Key( key: K4, state: KeyState.Up), A_Key( key: K5, state: KeyState.Up), A_Key( key: K6, state: KeyState.Up), A_Key( key: K7, state: KeyState.Up), A_Key( key: K8, state: KeyState.Up), A_Key( key: K9, state: KeyState.Up), A_Key( key: K0, state: KeyState.Up), A_Key( key: Space, state: KeyState.Up), A_Key( key: Tab, state: KeyState.Up), A_Key( key: Key.Up, state: KeyState.Up), A_Key( key: Key.Down, state: KeyState.Up), A_Key( key: Left, state: KeyState.Up), A_Key( key: Right, state: KeyState.Up), A_Key( key: F1, state: KeyState.Up), A_Key( key: F2, state: KeyState.Up), A_Key( key: F3, state: KeyState.Up), A_Key( key: F4, state: KeyState.Up), A_Key( key: F5, state: KeyState.Up), A_Key( key: F6, state: KeyState.Up), A_Key( key: F7, state: KeyState.Up), A_Key( key: F8, state: KeyState.Up), A_Key( key: F9, state: KeyState.Up), A_Key( key: F10, state: KeyState.Up), A_Key( key: F11, state: KeyState.Up), A_Key( key: F12, state: KeyState.Up), A_Key( key: LSuper, state: KeyState.Up), A_Key( key: RSuper, state: KeyState.Up), A_Key( key: LCtrl, state: KeyState.Up), A_Key( key: RCtrl, state: KeyState.Up), A_Key( key: LShift, state: KeyState.Up), A_Key( key: RShift, state: KeyState.Up), A_Key( key: LAlt, state: KeyState.Up), A_Key( key: RAlt, state: KeyState.Up), A_Key( key: Minus, state: KeyState.Up), A_Key( key: Equals, state: KeyState.Up), A_Key( key: BackSpace, state: KeyState.Up), A_Key( key: LBracket, state: KeyState.Up), A_Key( key: RBracket, state: KeyState.Up), A_Key( key: Enter, state: KeyState.Up), A_Key( key: SemiColon, state: KeyState.Up), A_Key( key: Apos, state: KeyState.Up), A_Key( key: Tilde, state: KeyState.Up), A_Key( key: BSlash, state: KeyState.Up), A_Key( key: Comma, state: KeyState.Up), A_Key( key: Period, state: KeyState.Up), A_Key( key: FSlash, state: KeyState.Up), A_Key( key: Caps, state: KeyState.Up), A_Key( key: Esc, state: KeyState.Up), A_Key( key: NPMult, state: KeyState.Up), A_Key( key: NumLock, state: KeyState.Up), A_Key( key: Scrl, state: KeyState.Up), A_Key( key: NP7, state: KeyState.Up), A_Key( key: NP8, state: KeyState.Up), A_Key( key: NP9, state: KeyState.Up), A_Key( key: NPMin, state: KeyState.Up), A_Key( key: NP4, state: KeyState.Up), A_Key( key: NP5, state: KeyState.Up), A_Key( key: NP6, state: KeyState.Up), A_Key( key: NPAdd, state: KeyState.Up), A_Key( key: NP1, state: KeyState.Up), A_Key( key: NP2, state: KeyState.Up), A_Key( key: NP3, state: KeyState.Up), A_Key( key: NP0, state: KeyState.Up), A_Key( key: NPDeci, state: KeyState.Up), A_Key( key: NPEnter, state: KeyState.Up), A_Key( key: NPDiv, state: KeyState.Up), A_Key( key: Prnt, state: KeyState.Up), A_Key( key: Home, state: KeyState.Up), A_Key( key: PgUp, state: KeyState.Up), A_Key( key: End, state: KeyState.Up), A_Key( key: PgDown, state: KeyState.Up), A_Key( key: Ins, state: KeyState.Up), A_Key( key: Del, state: KeyState.Up), A_Key( key: Pause, state: KeyState.Up), A_Key( key: Menu, state: KeyState.Up), A_Key( key: NPEquals, state: KeyState.Up)]proc state*(k: Key): KeyState = return keys[k.ord].statewhen defined linux:proc key_from_sym*(sym: KeySym): A_Key =case symof XK_aa: return keys[0]of XK_bb: return keys[1]of XK_cc: return keys[2]of XK_dd: return keys[3]of XK_ee: return keys[4]of XK_ff: return keys[5]of XK_gg: return keys[6]of XK_hh: return keys[7]of XK_ii: return keys[8]of XK_jj: return keys[9]of XK_kk: return keys[10]of XK_ll: return keys[11]of XK_mm: return keys[12]of XK_nn: return keys[13]of XK_oo: return keys[14]of XK_pp: return keys[15]of XK_qq: return keys[16]of XK_rr: return keys[17]of XK_ss: return keys[18]of XK_tt: return keys[19]of XK_uu: return keys[20]of XK_vv: return keys[21]of XK_ww: return keys[22]of XK_xx: return keys[23]of XK_yy: return keys[24]of XK_zz: return keys[25]of XK_1: return keys[26]of XK_2: return keys[27]of XK_3: return keys[28]of XK_4: return keys[29]of XK_5: return keys[30]of XK_6: return keys[31]of XK_7: return keys[32]of XK_8: return keys[33]of XK_9: return keys[34]of XK_0: return keys[35]of XK_space: return keys[36]of XK_Tab: return keys[37]of XK_Up: return keys[38]of XK_Down: return keys[39]of XK_Left: return keys[40]of XK_Right: return keys[41]of XK_F1: return keys[42]of XK_F2: return keys[43]of XK_F3: return keys[44]of XK_F4: return keys[45]of XK_F5: return keys[46]of XK_F6: return keys[47]of XK_F7: return keys[48]of XK_F8: return keys[49]of XK_F9: return keys[50]of XK_F10: return keys[51]of XK_F11: return keys[52]of XK_F12: return keys[53]of XK_Super_L: return keys[54]of XK_Super_R: return keys[55]of XK_Control_L: return keys[56]of XK_Control_R: return keys[57]of XK_Shift_L: return keys[58]of XK_Shift_R: return keys[59]of XK_Alt_L: return keys[60]of XK_Alt_R: return keys[61]of XK_minus: return keys[62]of XK_equal: return keys[63]of XK_BackSpace: return keys[64]of XK_bracketleft: return keys[65]of XK_bracketright: return keys[66]of XK_Return: return keys[67]of XK_semicolon: return keys[68]of XK_apostrophe: return keys[69]of XK_grave: return keys[70]of XK_backslash: return keys[71]of XK_comma: return keys[72]of XK_period: return keys[73]of XK_slash: return keys[74]of XK_Caps_Lock: return keys[75]of XK_Escape: return keys[76]#of XK_exclam:#of XK_quotedbl:#of XK_numbersign:#of XK_dollar:#of XK_percent:#of XK_ampersand:# of XK_apostrophe:# of XK_quoteright:# of XK_parenleft:# of XK_parenright:# of XK_asterisk:# of XK_plus:# of XK_comma:# of XK_minus:# of XK_period:# of XK_slash:# of XK_colon:# of XK_semicolon:# of XK_less:# of XK_equal:# of XK_greater:# of XK_question:# of XK_at:# of XK_A:# of XK_B:# of XK_C:# of XK_D:# of XK_E:# of XK_F:# of XK_G:# of XK_H:# of XK_I:# of XK_J:# of XK_K:# of XK_L:# of XK_M:# of XK_N:# of XK_O:# of XK_P:# of XK_Q:# of XK_R:# of XK_S:# of XK_T:# of XK_U:# of XK_V:# of XK_W:# of XK_X:# of XK_Y:# of XK_Z:# of XK_bracketleft:# of XK_backslash:# of XK_bracketright:# of XK_asciicircum:# of XK_underscore:# of XK_grave:# of XK_quoteleft:# of #:# of #:# of XK_braceleft:# of XK_bar:# of XK_braceright:# of XK_asciitilde:# of XK_nobreakspace:else: discardwhen defined linux:proc theLayoutName*( group: xkb_layout_index_t, xkbkm: ptr xkb_keymap): cstring =xkb_keymap_layout_get_name(xkbkm, xkb_layout_index_t group )proc theLocale*: string =when defined linux:# TODO: Better (more robust) way to get locale?if getEnv("LANG").len != 0:#echo "valid: LANG: ", getEnv("LANG")result = getEnv("LANG")elif getEnv("LC_ALL").len != 0:#echo "valid: LC_ALL"result = getEnv("LC_ALL")elif getEnv("LC_CTYPE").len != 0:#echo "valid: LC_CTYPE"result = getEnv("LC_CTYPE")else:result = "C"echo "WARNING: Couldn't find locale, falling back to 'C'"when defined linux:var #TODO: do we need the global pragmas?xkb_base_event* {.global.} : uint8xkb_base_error* {.global.} : uint8proc x11KeySymName*(ks: xcb_keysym_t): cstring =discard xkb_keysym_get_name(ks, result, sizeof(cstring).csize_t)resultproc loadComposeTable*( locale: string, xkbCompTable: var ptr xkb_compose_table, xkbContext: ptr xkb_context, xkbCompState: var ptr xkb_compose_state): bool =#xkb_compose_table_unref addr p.xkbCompTable#echo "Locale: ", localexkbCompTable = xkb_compose_table_new_from_locale(xkbContext, locale, xkb_compose_compile_flags 0)assert not xkbCompTable.isNilvar newCompState: ptr xkb_compose_state = xkb_compose_state_new(xkbCompTable, xkb_compose_state_flags 0)assert not newCompState.isNil#xkb_compose_state_unref addr p.xkbCompStatexkbCompState = newCompStatereturn trueproc loadKeyMap*( conn: ptr xcb_connection_t, xkbContext: var ptr xkb_context, xkbkm: var ptr xkb_keymap, xkbState: var ptr xkb_state, xkbNewState: var ptr xkb_state) =xkbContext = xkb_context_new xkb_context_flags 0assert not xkbContext.isNilxkb_keymap_unref xkbkmvar deviceID: int32 = xkb_x11_get_core_keyboard_device_id connxkbkm = xkb_x11_keymap_new_from_device( xkbContext, conn, deviceID, xkb_keymap_compile_flags 0)assert not xkbkm.isNilxkbNewState = xkb_x11_state_new_from_device(xkbkm, conn, deviceID)assert not xkbNewState.isNilxkb_state_unref xkbStatexkbState = xkbNewStateassert not xkbNewState.isNil#echo "DeviceID: ", deviceIDproc loadXKB*( dpy: PDisplay, conn: ptr xcb_connection_t# , group: var int8, firstXkbEvent: var uint8) =# keep ?# if XkbGetState(dpy, XkbUseCorekbd, addr state) == Success: group = state.group# discard XkbGetNames(dpy, XkbKeyNamesMask, desc)#discard XkbSetAutoRepeatRate(dpy, 1, 1, 1)# desc = XkbGetMap(dpy, 0, XkbUseCoreKbd)#kb: X11kbvarsupported: boolxkbDesc: ptr XkbDescRec = XkbAllocKeyboard()discard XAutoRepeatOn dpydiscard XkbSetDetectableAutoRepeat(dpy, true, addr supported)assert supporteddiscard XkbSelectEventDetails(dpy, XkbUseCorekbd, XkbStateNotify, XkbGroupStateMask, XkbGroupStateMask)assert xkb_x11_setup_xkb_extension( conn, uint16 1 #XKB_X11_MIN_MAJOR_XKB_VERSION, uint16 0 #XKB_X11_MIN_MINOR_XKB_VERSION, xkb_x11_setup_xkb_extension_flags 0, nil, nil, addr firstXkbEvent, nil) == 1discard XkbGetControls(dpy, XkbAllControlsMask, xkbDesc)discard XkbGetNames(dpy, XkbSymbolsNameMask, xkbDesc)discard XkbGetNames(dpy, XkbGroupNamesMask, xkbDesc)#echo xkbDesc.names.groupsconstrequired_map_parts: xcb_xkb_map_part_t =xcb_xkb_map_part_t XCB_XKB_MAP_PART_KEY_TYPES.ord orXCB_XKB_MAP_PART_KEY_SYMS.ord orXCB_XKB_MAP_PART_MODIFIER_MAP.ord orXCB_XKB_MAP_PART_EXPLICIT_COMPONENTS.ord orXCB_XKB_MAP_PART_KEY_ACTIONS.ord orXCB_XKB_MAP_PART_KEY_BEHAVIORS.ord orXCB_XKB_MAP_PART_VIRTUAL_MODS.ord orXCB_XKB_MAP_PART_VIRTUAL_MOD_MAP.ordrequired_events: xcb_xkb_event_type_t =xcb_xkb_event_type_t XCB_XKB_EVENT_TYPE_MAP_NOTIFY.ord orXCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY.ord orXCB_XKB_EVENT_TYPE_STATE_NOTIFY.orddiscard xcb_xkb_select_events( conn, xcb_xkb_device_spec_t xkb_x11_get_core_keyboard_device_id conn, uint16 required_events, 0, uint16 required_events, uint16 required_map_parts, uint16 required_map_parts, nil #cast[pointer](0))var#xkbs: ptr xkb_state#xkb_base_event: uint8xkberr: uint8xcbXKBRep = xcb_get_extension_data(conn, addr xcb_xkb_id)#echo xcbXKBRep.first_eventassert xkb_x11_setup_xkb_extension( conn, 1, 14, XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, nil, nil, addr firstXkbEvent, addr xkberr) == 1assert not xcbXKBRep.isNilproc is_down*(key: Key): bool = return bool keys[ord key].state == KeyState.Downproc is_down_or_held*(key: Key): bool = return bool (keys[ord key].state == KeyState.Down or keys[ord key].state == KeyState.Held)proc isUp*(key: Key): bool = return bool keys[ord key].state == KeyState.Up# snippets to possibly use later#[var st = xkb_state_get_keymap(p.xkbNewState)var group: xkb_layout_index_t = xkb_state_serialize_layout(p.xkbNewState, XKB_STATE_LAYOUT_EFFECTIVE)echo groupecho xkb_keymap_num_layouts stecho xkb_keymap_num_layouts stecho xkb_keymap_num_layouts stfor grp in 0..xkb_keymap_num_layouts(st):echo xkb_state_layout_index_is_active( p.xkbNewState,xkb_layout_index_t group, XKB_STATE_LAYOUT_EFFECTIVE)echo xkb_state_layout_index_is_active( p.xkbNewState,xkb_layout_index_t group, XKB_STATE_LAYOUT_DEPRESSED)echo xkb_state_layout_index_is_active( p.xkbNewState,xkb_layout_index_t group, XKB_STATE_LAYOUT_LATCHED)echo xkb_state_layout_index_is_active( p.xkbNewState,xkb_layout_index_t group, XKB_STATE_LAYOUT_LOCKED)]#
when defined linux:{.passl: "-lXinerama".}{.passl: "-lXrandr".}# {.passl: "-lXi".}# {.passl: "-lXcursor".}# {.passl: "-lXxf86vm".}import ../platforms/unix/x11/[x, xcb, xrandr,xlib]import ../utils/etc, retypeVideoModeObj = object of RootObjwidth*: int #! The width, in screen coordinates, of the video mode.height*: int #! The height, in screen coordinates, of the video mode.redBits*: int#! The bit depth of the red channel of the video mode.greenBits*: int #! The bit depth of the green channel of the video mode.blueBits*: int #! The bit depth of the blue channel of the video mode.refreshRate*: float32 #! The refresh rate, in Hz, of the video mode.bpp*:intVideoMode* = ref object of VideoModeObjGammaRampObj = object of RootObjredBits*: cushortgreenBits*: cushortblueBits*: cushortsize*: cuintGammaRamp* = ref object of GammaRampObjMonitorObj = object of RootObjrotation*: intwidthMM*: intheightMM*: intcurrVideoMode*: VideoModevideoModes*: seq[VideoMode]gammaRamp: GammaRampconnName*: stringname*: stringedid*: stringMonitor* = ref object of MonitorObj#, xcb#from utils import echor#proc monitorCount*(p: Monitors): int = p.monitorCountproc rgbFromDepth*( depth: int, actualDepth: bool = false # for later ?): array[3, int] =var delta: int#GLFW parity (???)#if depth == 32: depth = 24# R G Bresult[0] = (depth div 3)result[1] = (depth div 3)result[2] = (depth div 3)delta = depth - (result[0] * 3)if delta >= 1: result[1] += 1if delta == 2: result[0] += 1when defined linux:#TODO: handle when no monitor is marked a "primary"proc thePrimaryMonitor*( conn: ptr xcb_connection_t, root: ptr xcb_screen_t, win: xcb_window_t): Monitor =result = Monitor(currVideoMode: VideoMode())varsr = xcb_randr_get_screen_resources_current(conn, win)primaryCookie = xcb_randr_get_output_primary(conn, win)reply = xcb_randr_get_screen_resources_current_reply( conn, sr, nil)ts = reply.timestampprimary = xcb_randr_get_output_primary_reply(conn, primaryCookie, err0)primaryOut: xcb_randr_get_output_info_cookie_t = xcb_randr_get_output_info(conn, primary.output, ts)primaryInfo = xcb_randr_get_output_info_reply( conn, primaryOut, nil)primaryCRTC = xcb_randr_get_crtc_info(conn, primaryInfo.crtc, ts)actual = xcb_randr_get_crtc_info_reply(conn, primaryCRTC, err0)geo = xcb_get_geometry(conn, win)geoRep = xcb_get_geometry_reply(conn, geo, err0)outs = xcb_randr_get_screen_resources_current_outputs replything10 = xcb_randr_get_crtc_info_outputs actualoutProps = xcb_randr_list_output_properties(conn, primary.output)outPropsRep = xcb_randr_list_output_properties_reply(conn, outProps, err0)theAtoms = xcb_randr_list_output_properties_atoms(outpropsRep)far = xcb_randr_get_output_info_modes(primaryInfo)farLen = xcb_randr_get_output_info_modes_length(primaryInfo)nthis = xcb_randr_get_screen_info(conn, win)woo = xcb_randr_get_screen_info_reply(conn, nthis ,err0)woop = xcb_randr_get_screen_info_rates_iterator(woo)woopLen = xcb_randr_get_screen_info_rates_length(woo)boop = xcb_randr_get_monitors(conn, win, 1)boopRep = xcb_randr_get_monitors_reply(conn, boop, err0)boopIter = xcb_randr_get_monitors_monitors_iterator(boopRep)boopLen = xcb_randr_get_monitors_monitors_length(boopRep)amo = xcb_randr_get_screen_resources_current_outputs_length(reply)ops = xcb_randr_get_screen_resources_current_outputs(reply)modesLen = xcb_randr_get_screen_resources_current_modes_length(reply)modes = xcb_randr_get_screen_resources_current_modes(reply)for x in 0 ..< modesLen:if modes[x].id == actual.mode:result.currVideoMode.refreshRate = (modes[x].dot_clock.float32 /(modes[x].htotal.float32 *modes[x].vtotal.float32))result.currVideoMode.bpp = int geoRep[].depthresult.currVideoMode.width = int geoRep[].widthresult.currVideoMode.height = int geoRep[].height# rgbFromDepth( result.currVideoMode.bpp# , result.currVideoMode.redBits# , result.currVideoMode.greenBits# , result.currVideoMode.blueBits# )result.rotation = int (actual[].addr).rotationresult.widthMM = int primaryInfo.mm_widthresult.heightMM = int primaryInfo.mm_heightresult.connName = $cast[cstring](xcb_randr_get_output_info_name primaryInfo)for x in 0 ..< theAtoms[]:varz = xcb_get_atom_name(conn, theAtoms[] + x)zr = xcb_get_atom_name_reply(conn, z, err0)#echo xcb_get_atom_name_name(zr) # print atom nameif xcb_get_atom_name_name(zr) == "EDID":varatom = xcb_intern_atom(conn, 0, 4, "EDID")atomRep = xcb_intern_atom_reply(conn, atom, err0)b1 = xcb_randr_get_output_property(conn, primary.output, atomRep.atom, XCB_ATOM_ANY.ord, 0, 100, 0, 0)a1 = xcb_randr_get_output_property_reply(conn, b1, err0)propData = xcb_randr_get_output_property_data(a1)var str = newString(a1.num_items)copyMem(str[0].addr, propData, a1.num_items)result.edid = strresult.name = str.findAll(re"(\w+ \w+)")[0]proc theMonitors*( conn: ptr xcb_connection_t, root: ptr xcb_screen_t, win: xcb_window_t) =#: seq[Monitor] =varsr = xcb_randr_get_screen_resources_current(conn, win)reply = xcb_randr_get_screen_resources_current_reply( conn, sr, nil)outs: ptr xcb_randr_output_t = xcb_randr_get_screen_resources_current_outputs replylenn = xcb_randr_get_screen_resources_current_outputs_length replyfor i in 0 ..< lenn:var outt = xcb_randr_get_output_info_reply( conn, xcb_randr_get_output_info(conn, (outs + i)[], reply.timestamp), nil)if not outt.isNil:vargetcrtc = xcb_randr_get_crtc_info( conn, outt.crtc, reply.timestamp)crtc = xcb_randr_get_crtc_info_reply( conn, getcrtc, err0)if not crtc[].addr.isNil:echo crtc[]# printf("x = %d | y = %d | w = %d | h = %d\n",# crtc.x, crtc.y, crtc.width, crtc.height)# Relays* = ref object# pos: WindowPosRelay# size: WindowSizeRelay# close: WindowCloseRelay# refresh: WindowRefreshRelay# focus: WindowFocusRelay# iconify: WindowIconifyRelay# maximize: WindowMaximizedRelay# fbsize: WindowFBSizeRelay# scale: WindowContentScaleRelay# mouseButton: MouseButtonRelay# cursorPos: MouseCursorPosRelay# cursorEnter: MouseCursorEnterRelay# scroll: MouseScrollRelay# key: KeyboardRelay# character: CharRelay# charmods: CharModsRelay# drop: WindowDropRelay# MonitorX11* = ref object# output*: RROutput# crtc*: RRCrtc# oldMode*: RRMode# index: int # Index of corresponding Xinerama screen, for EWMH full screen window placement# Monitor* = ref object# name: cstring #array[128,char]# userPointer: pointer# widthMM, heightMM: int # Physical dimensions in millimeters.# window: Window# The window whose video mode is current on this monitor# modes: seq[Vidmode]# modeCount: int# currentMode: Vidmode# originalRamp: Gammaramp# currentRamp: Gammaramp# platform: MonitorType #We're assuming x11 monitor until this is truly cross-plat# x11*: MonitorX11# const# xineramaLib = "libXinerama.so"# Rotate0* = 1# Rotate90* = 2# Rotate180* = 4# Rotate270* = 8# ReflectX* = 16# ReflectY* = 32# type# PXineramaScreenInfo* = ptr XineramaScreenInfo# XineramaScreenInfo*{.final.} = object# screen_number*: cint# x_org*: int16# y_org*: int16# width*: int16# height*: int16# proc XineramaQueryExtension*(dpy: PDisplay, event_base: Pcint, error_base: Pcint): bool{.cdecl, importc.}# proc XineramaQueryVersion*(dpy: PDisplay, major: Pcint, minor: Pcint): int #[Status]# {.cdecl, importc.}# proc XineramaIsActive*(dpy: PDisplay): bool{.cdecl, dynlib: xineramaLib, importc.}# proc XineramaQueryScreens*(dpy: PDisplay, number: Pcint): PXineramaScreenInfo{.cdecl, importc.}# proc XRRGetOutputPrimary*(dpy: ptr Display, window: Window): RROutput {.cdecl, importc.}# proc thePrimary*( p: PDisplay# , w: Window# ) =# var# t = p.XRRGetScreenResourcesCurrent w# primary = p.XRRGetOutputPrimary w# echo repr primaryproc pollMonitors*() = discard
typeMouseObj = object of RootObjposX*: intposY*: intMouse* = ref object of MouseObjMouseButton* {.size: int8.sizeof.} = enumLMBMMBRMBmb4mb5mb6mb7mb8mb9mb10mb11mb12mb13mb14mb15mb16mb17mb18mb19mb20MouseBtnObj = object of RootObjstate*: MouseBtnStateMouseBtn* = ref object of MouseBtnObjMouseBtnState* {.pure.} = enum Up Down Held ScrolledUp ScrolledDown PressedAndScrolledUp PressedAndScrolledDownMouseBtnAction* {.pure.} = enum Release Press ScrollUp ScrollDownvarmbs*: array[20, MouseBtn] = [ MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up), MouseBtn(state: MouseBtnState.Up)]proc state*(mb: MouseButton): MouseBtnState = mbs[ord mb].stateproc is_down*(mb: MouseButton): bool = return bool mbs[ord mb].state == MouseBtnState.Downproc isUp*(mb: MouseButton): bool = return bool mbs[ord mb].state == MouseBtnState.Up
#[ THINGS TO FIX LATER:]##{.experimental: "codeReordering".}{.experimental: "notnil".}when defined linux:import ../platforms/unix/x11/[ x, xlib, xcb, xkblib, xkbCommon, keysyms, xfixes, xcomposite, xrandr]import keyboard, mouse, monitor, portalObj, ../utils/[etc]import bitops, re, sequtils, sugar, tables# , utils# , ../vk/[vkTypes, swapchain, utils, vulkan]# , monitor# , ../scene/scene# , ../drawable/[shapes]# , ../scene/scene# , ../camera#, glmwhen defined windows:import winim, winim/lean, ../platforms/w64/[ events, utils, the_types, the_functions]proc title_is*( the_portal: ptr Portal, s: string) =when defined linux:discard xcb_change_property( p.conn, xcb_prop_mode_t.XCB_PROP_MODE_REPLACE.ord, p.window, 39, 31, s.sizeof.uint8, s.len.uint32, addr s[0])proc the_screen_info() = discard# TODO:# fix the bad(?) video modes for the monitors# Gamma stuff# figure out a solution to workareaproc a_portal*( width: uint16 = 1920, height: uint16 = 1080, title: string = "Portal"): Portal =when defined linux:vartitle = titlexdisplay = XOpenDisplay nil# ewmh: xcb_ewmh_connection_tconn = XGetXCBConnection xdisplaymask = XCB_GC_FOREGROUND.ord or XCB_GC_GRAPHICS_EXPOSURES.ordsetup: ptr xcb_setup_twmClassTitle = title & '\0' #& title & '\0'root: WindowscreenIter = xcb_setup_roots_iterator xcb_get_setup conn#scr: cint = screenIter.remscreen: ptr xcb_screen_t = screenIter.datawindow = xcb_generate_id conn# xwin = DefaultRootWindow xdisplay# ewmhCookie = xcb_ewmh_init_atoms(conn, addr ewmh)#ewmhRep = xcb_ewmh_init_atoms_replies(addr ewmh, ewmhCookie, err0)valueList = [screen.black_pixel,0]#ver = conn.xcb_randr_query_version_reply( xcb_randr_query_version(conn, 1, 6) , err0 ) # TODO: should it be 1.5 or 1.6???sr = xcb_randr_get_screen_resources_current(conn, screen.root)reply = xcb_randr_get_screen_resources_current_reply( conn, sr, nil)outs: ptr xcb_randr_output_t = xcb_randr_get_screen_resources_current_outputs replylenn = xcb_randr_get_screen_resources_current_outputs_length replymonitorAmount: intmodesLen = xcb_randr_get_screen_resources_current_modes_length(reply)modes = xcb_randr_get_screen_resources_current_modes(reply)primaryCookie = xcb_randr_get_output_primary(conn, screen.root)primary = xcb_randr_get_output_primary_reply(conn, primaryCookie, err0)primaryOut: xcb_randr_get_output_info_cookie_t = xcb_randr_get_output_info(conn, primary.output, reply.timestamp)primaryInfo = xcb_randr_get_output_info_reply( conn, primaryOut, nil)primaryCRTC = xcb_randr_get_crtc_info(conn, primaryInfo.crtc, reply.timestamp)actualPrimary = xcb_randr_get_crtc_info_reply(conn, primaryCRTC, err0)# primaryModesInfo = xcb_randr_get_output_info_modes(primaryInfo)# primaryModesInfoReply = xcb_randr_get_output_info_modes_length(primaryInfo)#primaryOutputInfoModes = xcb_randr_get_output_info_modes_length()monitors: seq[Monitor]primaryMonitor: MonitortheRandrModes: seq[xcb_randr_mode_info_t]# TODO: stop being stupid and do it properly in the monitorAdd loopfor x in 0 ..< modesLen:theRandrModes.add modes[x]#theRandrModes.sortmask = XCB_CW_BACK_PIXEL or XCB_CW_EVENT_MASKvalueList[1] = XCB_NONE orXCB_EVENT_MASK_STRUCTURE_NOTIFY orXCB_EVENT_MASK_ENTER_WINDOW orXCB_EVENT_MASK_POINTER_MOTION_HINT orXCB_EVENT_MASK_BUTTON_1_MOTION orXCB_EVENT_MASK_BUTTON_2_MOTION orXCB_EVENT_MASK_BUTTON_3_MOTION orXCB_EVENT_MASK_BUTTON_4_MOTION orXCB_EVENT_MASK_BUTTON_5_MOTION orXCB_EVENT_MASK_EXPOSURE orXCB_EVENT_MASK_BUTTON_PRESS orXCB_EVENT_MASK_BUTTON_RELEASE orXCB_EVENT_MASK_BUTTON_MOTION orXCB_EVENT_MASK_POINTER_MOTION orXCB_EVENT_MASK_LEAVE_WINDOW orXCB_EVENT_MASK_KEY_PRESS orXCB_EVENT_MASK_KEY_RELEASE orXCB_MAP_NOTIFY orXCB_MAPPING_NOTIFY orXCB_MAP_REQUEST orXCB_EVENT_MASK_PROPERTY_CHANGE orXCB_EVENT_MASK_FOCUS_CHANGE orXCB_EVENT_MASK_KEYMAP_STATE orXCB_EVENT_MASK_VISIBILITY_CHANGE #or# TODO: This complicates how screen.width|height is reported and complicates the viewport size# as it combines 2+ monitors together# not necessarily needed to handle resizing, but is it better™?# XCB_EVENT_MASK_RESIZE_REDIRECTif xcb_connection_has_error(conn).bool: quit "ERROR: xcb conn"discard xcb_create_window( conn # connection, 0 # depth (same as root), window # window ID, screen.root # parent window, 0 # X, 0 # Y, width, height, 0 #border width, xcb_window_class_t.XCB_WINDOW_CLASS_INPUT_OUTPUT.uint16, screen.root_visual, mask.uint32, valueList[0].addr)varev: xcb_generic_event_tcookie = xcb_intern_atom(conn, 1, 12,"WM_PROTOCOLS")cookie2 = xcb_intern_atom(conn, 0, 16, "WM_DELETE_WINDOW")cookie3 = xcb_intern_atom(conn, 0, 15, "_MOTIF_WM_HINTS" )# maxvertAtom = xcb_intern_atom(conn, 0, 13, "_NET_WORKAREA")# maxHorzAtom = xcb_intern_atom(conn, 0, 28, "_NET_WM_STATE_MAXIMIZED_HORZ")#something = xcb_get_property(conn, 0, window, x_NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW.ord, 0, 1024)reply1 = xcb_intern_atom_reply(conn, cookie, err0)reply2 = xcb_intern_atom_reply(conn, cookie2, err0)reply3 = xcb_intern_atom_reply( conn, cookie3, err0)# replyMaxV = xcb_intern_atom_reply( conn, maxvertAtom, err0)# replyMaxH = xcb_intern_atom_reply( conn, maxhorzAtom, err0)# replyMaxVProp = xcb_get_property( conn, 0, window, replyMaxV.atom, XCB_ATOM_CARDINAL.ord, 0, 1024)# replyMaxHProp = xcb_get_property( conn, 0, window, replyMaxH.atom, XCB_ATOM_STRING.ord, 0, 0)# vRep = xcb_get_property_reply(conn, replyMaxVProp, err0)# hRep = xcb_get_property_reply(conn, replyMaxHProp, err0)colors = rgbFromDepth screen.root_depth.intatomEdid = xcb_intern_atom(conn, 0, 4, "EDID")atomEdidRep = xcb_intern_atom_reply(conn, atomEdid, err0)# atomWorkArea = xcb_intern_atom(conn, 0, 13, "_NET_WORKAREA")# atomWARep = xcb_intern_atom_reply(conn, atomWorkArea, err0)decoratedHint = MotifHints( flags: MWM_HINTS_FUNCTIONS or MWM_HINTS_DECORATIONS, functions: MWM_FUNC_MOVE or MWM_FUNC_MINIMIZE or MWM_FUNC_CLOSE or MWM_FUNC_RESIZE, decorations: MWM_DECOR_BORDER or MWM_DECOR_TITLE or MWM_DECOR_MINIMIZE or MWM_DECOR_MENU, input_mode: 0, status: 0)discard xcb_change_property( conn, xcb_prop_mode_t.XCB_PROP_MODE_REPLACE.ord, window, reply1.atom, 4, 32, 1, addr reply2.atom)# setting window titlediscard xcb_change_property( conn, xcb_prop_mode_t.XCB_PROP_MODE_REPLACE.ord, window, 39, 31, title.sizeof.uint8, title.len.uint32, addr title[0])# WM_CLASS property (?????)discard xcb_change_property( conn, xcb_prop_mode_t.XCB_PROP_MODE_REPLACE.ord, window, 39, 31, wmClassTitle.sizeof.uint8, wmClassTitle.len.uint32, addr wmClassTitle[0])discard xcb_change_property( conn, xcb_prop_mode_t.XCB_PROP_MODE_REPLACE.ord, window, reply3.atom, reply3.atom, 32, 5, addr decoratedHint)# this will get connected monitors# but xcb_randr_get_output_info_reply will still report monitors even if they're disabled# so we get the CRTC, which will only(?) get currently enabled monitors# so we more accurately know what the user currently has# NOTE:# Currently, just using the depth, from the geoReply, which is from the overall "Screen" (combined render area from both monitors(?))# Maybe in the future, properly get the depth, by individual monitorfor x in 0 ..< lenn:varoutt = xcb_randr_get_output_info_reply(conn, xcb_randr_get_output_info(conn, outs[x], reply.timestamp), nil )outCR = xcb_randr_get_crtc_info(conn, outt.crtc, reply.timestamp)outActual = xcb_randr_get_crtc_info_reply(conn, outCR, err0)outmodes = xcb_randr_get_output_info_modes outtoutmodeslen = xcb_randr_get_output_info_modes_length outt# TODO: do we really need the connection check if we're getting a CRTC reply?if outt.connection == RR_Connected andnot outActual.isNil:for m in 0 ..< modesLen:if modes[m].id == outActual.mode:varb1 = xcb_randr_get_output_property( conn, outs[x], atomEdidRep.atom, XCB_ATOM_ANY.ord, 0, 100, 0, 0)a1 = xcb_randr_get_output_property_reply(conn, b1, err0)propData = xcb_randr_get_output_property_data(a1)edidStr = newString(a1.num_items)copyMem( edidStr[0].addr, propData, a1.num_items)varactualvTotal: float32 = cdouble modes[m].vtotalif bitand(modes[m].mode_flags, RR_DoubleScan).bool: actualvTotal *= 2if bitand(modes[m].mode_flags, RR_Interlace).bool: actualvTotal /= 2varaMonitor = Monitor( currVideoMode: VideoMode( refreshRate: ( modes[m].dot_clock.float32 /( modes[m].htotal.float32 *actualvTotal.float32)), width: int outActual.width, height: int outActual.height, bpp: int screen.root_depth, redbits: colors[0], greenbits: colors[1], bluebits: colors[2]), rotation : int (outActual[].addr).rotation, widthMM : int outt.mm_width, heightMM : int outt.mm_height, connName : $cast[cstring](xcb_randr_get_output_info_name outt), edid: edidStr, name: (edidstr.findAll(re"(\w+ \w+)\n")[0])[0..^2] # chop the `\n` off at the end)#add all the display modes!#TODO: investigate the weird video modes with high refreshratesfor mmm, theMode in theRandrModes:for mmm2 in 0 ..< outmodesLen:if theMode.id == outmodes[mmm2]:var theActualvTotal: float32 = cdouble theMode.vtotalif bitand(modes[m].mode_flags, RR_DoubleScan).bool: actualvTotal *= 2if bitand(modes[m].mode_flags, RR_Interlace).bool: actualvTotal /= 2var aVMode = VideoMode( refreshRate: (modes[m].dot_clock.float32 /( modes[m].htotal.float32 *theActualvTotal.float32)), width: int theMode.width, height: int theMode.height, bpp: int screen.root_depth, redbits: colors[0], greenbits: colors[1], bluebits: colors[2])aMonitor.videoModes.add aVMode#echo aMonitor.name, " can: ", repr theModemonitorAmount += 1monitors.add aMonitor#TODO: a better way to check(verify?) the primary monitorif outActual.x == actualPrimary.x andoutActual.y == actualPrimary.y andoutActual.width == actualPrimary.width andoutActual.height == actualPrimary.height:primaryMonitor = aMonitorelse: discard#echo repr modes[m]discard xcb_map_window(conn, window)#echo repr monitorsxcb_aux_sync connvarxcb_xfixes_id2 = xcb_extension_t( name: "XFIXES", global_id: 0)p = Portal( width: width, height: height, title: title, reply1: reply1, reply2: reply2, conn: conn, window: window, setup: setup, xdisplay: xdisplay, screen: screen, currMonitor: primaryMonitor, monitors: monitors# , screenIter: iter, locale: theLocale(), mouse: Mouse()#, xwindow: DefaultRootWindow xdiplay, xcbReply: conn.xcb_get_extension_data( addr xcb_xfixes_id2)#, xkbCompState: addr xkbcs#, kb: kb#, relays: Relays(key_relay: Key_Relay())#, group: group, root: root# , iter: iter)p.xdisplay.loadXKB(p.conn, p.firstXkbEvent)assert p.xdisplay.XCompositeQueryExtension(addr p.xcompEvent, addr p.xCompError) == trueassert p.xdisplay.XFixesQueryExtension(addr p.xfixes_event, addr p.xfixes_error) == truep.xdisplay.XFixesSelectCursorInput( p.xwindow, XFixesDisplayCursorNotifyMask)var xfixesMask: uint32 = XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE.ord orXCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY.ord orXCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER.orddiscard xcb_xfixes_select_selection_input_checked(p.conn, p.window, xcb_atom_t XCB_ATOM_PRIMARY.ord, xfixesMask)discard xcb_xfixes_select_selection_input_checked(p.conn, p.window, xcb_atom_t XCB_ATOM_SECONDARY.ord, xfixesMask)p.wmState = xcb_intern_atom(conn, 0, 13, "_NET_WM_STATE" )p.wmHidden = xcb_intern_atom(conn, 0, 20, "_NET_WM_STATE_HIDDEN" )p.wmMaxY = xcb_intern_atom(conn, 0, 28, "_NET_WM_STATE_MAXIMIZED_VERT" )p.wmMaxX = xcb_intern_atom(conn, 0, 28, "_NET_WM_STATE_MAXIMIZED_HORZ" )#p.getDPI()when defined windows:varh_instance = GetModuleHandle nilappName = "portal!"hwnd: lean.HWNDwndclass: WNDCLASSp = Portal( width: width, height: height, title: title)GetModuleHandleExW( bitor( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT), cast[ptr WCHAR] (addr appName), cast[ptr HMODULE] (addr h_instance))wndclass.style = CS_HREDRAW or CS_VREDRAWwndclass.lpfnWndProc = WindowProcwndclass.cbClsExtra = cast[int32] (( Portal).sizeof)wndclass.cbWndExtra = cast[int32] ((Portal).sizeof)wndclass.hInstance = h_instancewndclass.hIcon = LoadIcon(0, IDI_APPLICATION)wndclass.hCursor = LoadCursor(0, IDC_ARROW)wndclass.hbrBackground = GetStockObject(WHITE_BRUSH)wndclass.lpszMenuName = nilwndclass.lpszClassName = appNameRegisterClass(wndclass)hwnd = CreateWindow( appName, "p0", WS_VISIBLE or WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, h_instance, addr (p))discard ShowWindow(hwnd, SW_SHOW)discard UpdateWindow(hwnd)var lp_string: LPWSTR = "Windows did a bad"if GetLastError() != 0: MessageBox( 0, T $GetLastError().why, lp_string # TODO ( T"some_string" ) macro gives an error, 0)p.hwnd = hwndp.wndclass = wndclassp.hInstance = h_instancevarthing = 98thing2: ptr int = addr thing#[if SetProp( GetActiveWindow(), "thing2", cast[HANDLE] ((thing2))) == 0: quit("SetProp thing2 failed")var get_thing: pointer = cast[pointer] ( GetProp( GetActiveWindow() , "thing2") )echo "GETPROP INSIDE PORTAL:"echo repr (cast[ptr int](get_thing)[])echo "GETPROP PORTAL END/ "]#ChangeWindowMessageFilterEx( p.hwnd, WM_DROPFILES, MSGFLT_ALLOW, nil)ChangeWindowMessageFilterEx( p.hwnd, WM_COPYDATA, MSGFLT_ALLOW, nil)#ChangeWindowMessageFilterEx(p.hwnd,WM_COPYGLOBALDATA, MSGFLT_ALLOW, nil);var ctrl =TINITCOMMONCONTROLSEX( dwSize: int32 sizeof(TINITCOMMONCONTROLSEX), dwICC: ICC_DATE_CLASSES or ICC_LISTVIEW_CLASSES orICC_INTERNET_CLASSES orICC_LINK_CLASS or ICC_BAR_CLASSES or ICC_COOL_CLASSES)InitCommonControlsEx(ctrl)OleInitialize(nil)p# TODO: get this inside keyboard.nim without recursive import errorswhen defined linux:proc update_key_state*( p: var Portal, key: KeySym, action: Key_Action) =#var held: bool#echo "[",key_from_sym(key).key, "]", " was: ", $key_from_sym(key).stateif key_from_sym(key).state == KeyState.Up andaction == Key_Action.Press: key_from_sym(key).state = KeyState.Downelif ( key_from_sym(key).state == KeyState.Down orkey_from_sym(key).state == KeyState.Held) andaction == Key_Action.Release: key_from_sym(key).state = KeyState.Upelif key_from_sym(key).state == KeyState.Down andaction == Key_Action.Press: key_from_sym(key).state = KeyState.Held#echo "[",key_from_sym(key).key, "]", " now is: ", $key_from_sym(key).state#echo ""p.relays.key_relay( p, key_from_sym(key).key, action.ord)when defined windows:proc update_key_state*( p: var Portal, key: WORD, action: Key_Action) = discard#var held: bool#echo "[",key_from_sym(key).key, "]", " was: ", $key_from_sym(key).state#[ if key_from_sym(key).state == KeyState.Up andaction == Key_Action.Press: key_from_sym(key).state = KeyState.Downelif ( key_from_sym(key).state == KeyState.Down orkey_from_sym(key).state == KeyState.Held) andaction == Key_Action.Release: key_from_sym(key).state = KeyState.Upelif key_from_sym(key).state == KeyState.Down andaction == Key_Action.Press: key_from_sym(key).state = KeyState.Held#echo "[",key_from_sym(key).key, "]", " now is: ", $key_from_sym(key).state#echo "" ]##[ p.relays.key_relay( p, key_from_sym(key).key, action.ord) ]## TODO:# 1. get this inside keyboard.nim without recursive import errors# 2. Handle multiple scroll wheels# 3. Do we actually care about MouseBtnState.PressedAndScrolledUp && MouseBtnState.PressedAndScrolledDownwhen defined linux:proc updateMouseKeyState*( the_portal: ptr Portal, mb: xcb_button_t, action: MouseBtnAction) =#[MouseBtnState* {.pure.} = enum Up Down Held ScrolledUp ScrolledDown PressedAndScrolledUp PressedAndScrolledDownMouseBtnAction* {.pure.} = enum Release Press ScrollUp ScrollDown]#case actionof MouseBtnAction.Press:mbs[mb - 1].state = MouseBtnState.Downof MouseBtnAction.Release:mbs[mb - 1].state = MouseBtnState.Upof MouseBtnAction.ScrollUp:mbs[1].state = MouseBtnState.ScrolledUpof MouseBtnAction.ScrollDown:mbs[1].state = MouseBtnState.ScrolledDown#else: discardp.relays.mouse_key_relay( p, MouseButton ord (mb - 1), action)# TODO:# 1. get this inside keyboard.nim without recursive import errors# 2. Handle multiple scroll wheels# 3. Do we actually care about MouseBtnState.PressedAndScrolledUp && MouseBtnState.PressedAndScrolledDownproc updateMouseKeyState*( the_portal: ptr Portal, mb: xcb_button_t, action: MouseBtnAction) =#[MouseBtnState* {.pure.} = enum Up Down Held ScrolledUp ScrolledDown PressedAndScrolledUp PressedAndScrolledDownMouseBtnAction* {.pure.} = enum Release Press ScrollUp ScrollDown]#case actionof MouseBtnAction.Press:mbs[mb - 1].state = MouseBtnState.Downof MouseBtnAction.Release:mbs[mb - 1].state = MouseBtnState.Upof MouseBtnAction.ScrollUp:mbs[1].state = MouseBtnState.ScrolledUpof MouseBtnAction.ScrollDown:mbs[1].state = MouseBtnState.ScrolledDown#else: discardp.relays.mouse_key_relay( p, MouseButton ord (mb - 1), action)proc events*( p: var Portal) =when defined linux:discard xcb_flush p.connvarcm: xcb_client_message_event_tev: ptr xcb_generic_event_tev = xcb_poll_for_event p.connif not ev.isNil:case ev.response_type and 0x7f#of XCB_RESIZE_REQUEST: discardof XCB_EXPOSE: discard nilof XCB_MOTION_NOTIFY:var me = cast[ptr xcb_motion_notify_event_t](ev)p.mouse.posX = me.event_xp.mouse.posY = me.event_yof XCB_BUTTON_PRESS:var bpe = cast[ptr xcb_button_press_event_t](ev)case bpe.detailof 4: p.updateMouseKeyState bpe.detail, MouseBtnAction.ScrollUpof 5: p.updateMouseKeyState bpe.detail, MouseBtnAction.ScrollDownelse: p.updateMouseKeyState bpe.detail, MouseBtnAction.Pressp.mouseRep = cast[ptr xcb_query_pointer_reply_t](ev)# need to subtract old mouse pos from newof XCB_BUTTON_RELEASE:var bpe = cast[ptr xcb_button_release_event_t](ev)p.updateMouseKeyState bpe.detail, MouseBtnAction.Releasep.mouseRep = cast[ptr xcb_query_pointer_reply_t](ev)of XCB_ENTER_NOTIFY: discard nilof XCB_LEAVE_NOTIFY: discard nilof XCB_KEY_PRESS:varkp: ptr xcb_key_press_event_t = cast[ ptr xcb_key_press_event_t](ev)#kcs: int16 = if bitand(kp.state, ShiftMask).bool: 1 else: 0#p.update_key_state(XkbKeycodeToKeysym(p.xdiplay, kp.detail.char, 0, kcs), Press)# TODO: this can fail sometimes?p.update_key_state(xkb_state_key_get_one_sym(p.xkbState, kp.detail), Key_Action.Press)of XCB_KEY_RELEASE:varkr: ptr xcb_key_release_event_t = cast[ ptr xcb_key_release_event_t](ev)#kcs: int16 = if bitand(kr.state, ShiftMask).bool: 1 else: 0p.update_key_state(xkb_state_key_get_one_sym(p.xkbState, kr.detail), Key_Action.Release)#of XCB_ALLOC_NAMED_COLOR: discardof XCB_MAP_NOTIFY:varxkbe = cast[ptr xkb_event](ev)#we = cast[ ptr xcb_map_notify_event_t](ev)#geo = p.conn.xcb_get_geometry_reply(p.conn.xcb_get_geometry p.window, err0)#echo geo[]p.conn.loadKeyMap( p.xkbContext, p.xkbkm, p.xkbState, p.xkbNewState)assert loadComposeTable( p.locale, p.xkbCompTable, p.xkbContext, p.xkbCompState) == truep.currKeyboardGroup = xkbe[].state_notify.groupp.relays.window_mapped_relay( p, 8, 4)of XCB_XKB_MAP_NOTIFY: discardof XCB_PROPERTY_NOTIFY:var pe = cast[ptr xcb_property_notify_event_t](ev)#if pe.atom == wmState: echo "?"of XCB_CONFIGURE_NOTIFY:varce = cast[ ptr xcb_configure_notify_event_t](ev)if ce.width > 0 and ce.height > 0:p.relays.window_resize_relay( p, int ce.width, int ce.height)of XCB_MAPPING_NOTIFY:var km: ptr xcb_mapping_notify_event_t= cast[ ptr xcb_mapping_notify_event_t](ev)#echo "mapping: MYA-NEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE!!!!~~~~"# of XCB_MAP_REQUEST:# var km: ptr xcb_map_request_event_t= cast[ ptr xcb_map_request_event_t](ev)# echor km# echo "map_request: MYA-NEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE!!!!~~~~"# of XCB_XKB_STATE_NOTIFY: echo "wo"#echo repr kmof XCB_CLIENT_MESSAGE:# this is the "window close" responseif ev.response_type == 161: p.quit = true#of XCB_INPUT_DEVICE_STATE_NOTIFY:#of XCB_DESTROY_NOTIFY: echo "window destroyed"of XCB_VISIBILITY_NOTIFY: discardof XCB_FOCUS_IN: discard #echo "focused"of XCB_FOCUS_OUT: discard #echo "un-focused"else:#echo repr ev#echo ev.response_type#echo ev.response_type, " <> ", p.firstXkbEventif ev.response_type == p.firstXkbEvent:var xkbe = cast[ptr xkb_event](ev)case xkbe.anyy.xkbTypeof XCB_XKB_NEW_KEYBOARD_NOTIFY:# check if things actually changed on the keyboard# TODO: This also triggers for a first press on a 2nd mouseecho "new keyboard!"# echo repr p.xkbState.componentsp.conn.loadKeyMap( p.xkbContext, p.xkbkm, p.xkbState, p.xkbNewState)of XCB_XKB_MAP_NOTIFY:echo "XCB_XKB_MAP_NOTIFY"of XCB_XKB_STATE_NOTIFY:#echo "XCB_XKB_STATE_NOTIFY"#TODO: figure a better way# Currently this event hits if any "mod" key is pressed like ctrl (4) or alt (8)# so we check for this and filter out# as we currently only care about getting a new keyboard layout/groupif xkbe[].state_notify.mods == 8 orxkbe[].state_notify.mods == 4 orxkbe[].state_notify.eventType == 5: discard # mouse button down (?)else:if xkbe[].state_notify.group != p.currKeyboardGroup:p.conn.loadKeyMap( p.xkbContext, p.xkbkm, p.xkbState, p.xkbNewState)p.group = xkb_state_serialize_layout(p.xkbNewState, XKB_STATE_LAYOUT_EFFECTIVE)p.currKeyboardLayout = toString theLayoutName(p.group, p.xkbkm)p.currKeyboardGroup = xkbe[].state_notify.groupof XCB_XKB_EVENT_TYPE_CONTROLS_NOTIFY.ord: discardof XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY.ord: discardof XCB_XKB_EVENT_TYPE_INDICATOR_MAP_NOTIFY.ord: discardof XCB_XKB_EVENT_TYPE_NAMES_NOTIFY.ord: discardof XCB_XKB_EVENT_TYPE_COMPAT_MAP_NOTIFY.ord: discard#of uint8 XCB_XKB_EVENT_TYPE_BELL_NOTIFY.ord: discard#of XCB_XKB_EVENT_TYPE_ACTION_MESSAGE.ord: discard#of XCB_XKB_EVENT_TYPE_ACCESS_X_NOTIFY.ord: discard#of XCB_XKB_EVENT_TYPE_EXTENSION_DEVICE_NOTIFY.ord: discardelse: discardif ev.response_type == (uint p.xfixesEvent + XFixesCursorNotify): discard# var ce = cast[ptr XFixesCursorNotifyEvent](ev)# echo repr ce# var e = cast[ptr XFixesCursorNotifyEvent](ev)# echo repr ewhen defined windows:varmsg: MSGwhile PeekMessageW( msg, cast[HWND] (nil), 0, 0, PM_REMOVE) != 0:#for hook_proc inTranslateMessage msgDispatchMessage msg
{.experimental: "codeReordering".}when defined linux:import ../platforms/unix/x11/[x,xcb, xlib, xkbCommon]when defined windows:import winim/leanimport ../platforms/w64/[the_types]import keyboard, mouse, monitorimport tablestypeNS = objectretina*: boolframeName*: array[256, char]X11 = objectclassName*: array[256, char]instanceName*: array[256, char]Win32_Key_Menu = objectkeymenu*: boolWL = objectappId*: array[256,char]Portal_Config* = ref objecttitle*: ptr charx_pos*: inty_pos*: intwidth*: intheight*: intresizable*: boolvisible*: booldecorated*: boolfocused*: boolautoIconify*: boolfloating*: boolmaximized*: boolcenterCursor*: boolfocusOnShow*: boolmousePassthrough*: boolscaleToMonitor*: boolns*: NSx11*: X11win32*: Win32_Key_Menuwl*: WLPortalObj* = object of RootObjrelays*: Relaysquit*: booltitle*: stringmonitorCount: intcurrMonitor*: Monitormonitors*: seq[Monitor]width*: uint16height*: uint16config*: Portal_Configwhen defined linux:conn*: ptr xcb_connection_twid*: uint32reply1*,reply2*: ptr xcb_intern_atom_reply_twindow*: xcb.xcb_window_t # is this root???root*: Windowiter*: xcb_screen_iterator_tsetup*: ptr xcb_setup_tuserPtr*: pointergroup*: xkb_layout_index_txdisplay*: xlib.PDisplay#kb*: X11KbxkbContext*: ptr xkb_contextxkbkm*: ptr xkb_keymapxkbState* : ptr xkb_statexkbNewState* : ptr xkb_statexkbCompState*: ptr xkb_compose_statexkbCompTable*: ptr xkb_compose_tablelocale*: stringscreen*: ptr xcb_screen_tscreenIter*: xcb_screen_iterator_tfirstXkbEvent*: uint8# TODO: find the """proper""" xcb/xkb function for getting group change# until then, we store the group, and compare it with the event's group# when the proper event fires, in order to check if we need to update layouts for an actually new groupcurrKeyboardGroup*: uint8# TODO: break this out into a proper `Keyboard` struct latercurrKeyboardLayout*: stringxwindow*: WindowxfixesEvent*: intxfixesError*: intxcompEvent*: intxcompError*: intxcbReply*: ptr xcb_query_extension_reply_tqp*: xcb_query_pointer_cookie_tmouseRep*: ptr xcb_query_pointer_reply_tmouse*: MousewmState*: xcb_intern_atom_cookie_twmHidden*: xcb_intern_atom_cookie_twmMaxX*: xcb_intern_atom_cookie_twmMaxY*: xcb_intern_atom_cookie_twinGeo*: WindowArea#clientGeo*: WindowArea#frameGeo*: WindowAreawhen defined windows:hwnd*: lean.HWNDmsg*: MSGwParam*: WPARAMlParam*: LPARAMwndclass*: WNDCLASSh_Instance*: HMODULEmain_window_class*: ATOMwin32_message_loop_hook_procs*: seq[Win32_Message_Loop_Hook_Proc]class_atom_table*: Table[string, ATOM]accelorator_exists*: boolw32_window_table*: Table[HWND, Win32_Window]win32_Instance*: HANDLEPortal* = ref object of PortalObj# # relays.nimtypeKey_Relay* = proc ( the_portal: ptr Portal, key: Key, action: int) {.closure.}Mouse_Key_Relay* = proc ( the_portal: ptr Portal, mb: MouseButton, action: MouseBtnAction) {.closure.}Window_Resize_Relay* = proc ( the_portal: ptr Portal, width: int, height: int) {.closure.}Window_Mapped_Relay* = proc ( the_portal: ptr Portal, width: int, height: int) {.closure.}Window_Closed_Relay* = proc ( the_portal: ptr Portal) {.closure.}Window_Input_Language_Change_Relay* = proc ( the_portal: ptr Portal) {.closure.}Relays* = objectwindow_closed_relay*: Window_Closed_Relay#key_relay*: Key_Relay#mouse_key_relay*: Mouse_Key_Relay#window_resize_relay*: Window_Resize_Relay#window_mapped_relay*: Window_Mapped_Relay#window_input_language_change_relay*: Window_Input_Language_Change_Relay#[ proc set_key_relay*( the_portal: ptr Portal, key_relay: Key_Relay) =#TODO: actual pointers like GLFWthe_portal.relays.key_relay = key_relayproc set_mouse_relay*( the_portal: ptr Portal, mouse_key_relay: Mouse_Key_Relay) = the_portal.relays.mouse_key_relay = mouse_key_relayproc set_window_resize_relay*( the_portal: ptr Portal, window_resize_relay: Window_Resize_Relay) = the_portal.relays.window_resize_relay = window_resize_relayproc set_window_mapped_relay*( the_portal: ptr Portal, window_mapped_relay: Window_Mapped_Relay) = the_portal.relays.window_mapped_relay = window_mapped_relay ]#proc set_window_closed_relay*( the_portal: ptr Portal, window_closed_relay: Window_Closed_Relay) = the_portal.relays.window_closed_relay = window_closed_relay#[ proc set_window_input_language_change_relay*( the_portal: ptr Portal, window_input_language_change_relay: Window_Input_Language_Change_Relay) = the_portal.relays.window_input_language_change_relay = window_input_language_change_relay ]##[void _glfwInputWindowCloseRequest(_GLFWwindow* window){assert(window != NULL);window->shouldClose = GLFW_TRUE;if (window->callbacks.close)window->callbacks.close((GLFWwindow*) window);}]#proc close_window*(p: var Portal) =when defined linux:xcb_disconnect p.conndiscard xcb_destroy_window( p.conn, p.window)when defined windows:p.quit = true
import portalObj, ../platforms/unix/x11/xcb#, ../scene/tysproc lock_window*( the_portal: ptr Portal, width: int, height: int) =when defined linux:var theHints = WMSizeHints( flags: WM_SIZE_HINT_P_MIN_SIZE.ord, minWidth: int32 width, minHeight: int32 height, maxWidth: int32 width, maxHeight: int32 height)discard xcb_change_property( p.conn, ord XCB_PROP_MODE_REPLACE, p.window, xcb_atom_t XCB_ATOM_WM_NORMAL_HINTS, xcb_atom_t XCB_ATOM_WM_SIZE_HINTS, 32, uint32 WMSizeHints.sizeof shr 2, addr theHints)proc toggle_full_screen*(the_portal: ptr Portal) =when defined linux:varcookie3 = xcb_intern_atom(p.conn, 0, 15, "_MOTIF_WM_HINTS" )reply3 = xcb_intern_atom_reply( p.conn, cookie3, err0)fullscreenHint = MotifHints( flags: 2, functions: 0, decorations: 0, input_mode: 0, status: 0)decoratedHint = MotifHints( flags: MWM_HINTS_FUNCTIONS or MWM_HINTS_DECORATIONS, functions: MWM_FUNC_MOVE or MWM_FUNC_MINIMIZE or MWM_FUNC_CLOSE, decorations: MWM_DECOR_BORDER or MWM_DECOR_TITLE or MWM_DECOR_MINIMIZE or MWM_DECOR_MENU, input_mode: 0, status: 0)if not p.isFullScreen:discard xcb_change_property( p.conn, xcb_prop_mode_t.XCB_PROP_MODE_REPLACE.ord, p.window, reply3.atom, reply3.atom, 32, 5, addr fullscreenHint)p.isFullScreen = trueelse:discard xcb_change_property( p.conn, xcb_prop_mode_t.XCB_PROP_MODE_REPLACE.ord, p.window, reply3.atom, reply3.atom #THIS is essential, 32, 5, addr decoratedHint)p.isFullScreen = false#[ proc top*(a: WindowArea): int16 = a.yproc left*(a: WindowArea): int16 = a.xproc right*(a: WindowArea): int16 = a.x + int16 a.widthproc bottom*(a: WindowArea): int16 = a.y + int16 a.heightproc `==`*(a, b: WindowArea): bool =a.x == b.x anda.y == b.y anda.width == b.width anda.height == b.height]#
surfaceCreateInfo.connection = cast[ptr nvk.xcb_connection_t](portal.conn)surfaceCreateInfo.window = cast[nvk.xcb_window_t](portal.window)
surfaceCreateInfo.connection = cast[ptr nvk.xcb_connection_t](wain.conn)surfaceCreateInfo.window = cast[nvk.xcb_window_t](wain.window)
surfaceCreateInfo.hinstance = cast[nvk.HINSTANCE] ( addr portal.h_instance )surfaceCreateInfo.hwnd = cast[nvk.HWND] (portal.hwnd)
surfaceCreateInfo.hinstance = cast[nvk.HINSTANCE] ( addr wain.h_instance )surfaceCreateInfo.hwnd = cast[nvk.HWND] (wain.hwnd)
#set_key_relay (addr the_portal), key_relay#set_mouse_relay(the_portal.addr, mouse_key_relay)#set_window_resize_relay (addr the_portal), window_resize_relay#set_window_mapped_relay (addr the_portal), window_mapped_relayset_window_closed_relay (addr the_portal), window_closed_relayecho repr the_portal.relays
set_key_relay( the_waindow[], key_relay)set_mouse_relay( the_waindow[], mouse_key_relay)
echo "created window!"echo repr lParamSetWindowLongPtr( hwnd, GWLP_USERDATA, cast[LONG_PTR]( lParam) )
echo "WM_CREATE"a_waindow = create(Waindow)a_waindow = cast[ptr Waindow]( the_struct.lpCreateParams)SetWindowLongPtr( hwnd, GWLP_USERDATA, cast[LONG_PTR]( a_waindow) )
of WM_KEYUP , WM_KEYDOWN, WM_SYSKEYDOWN, WM_SYSKEYUP:
of WM_KEYUP ,WM_SYSKEYUP:varvirtual_key_code: WORD = LOWORD(wParam)the_wain_key = wain_keyboard_key_from_windows_virtual_key_code(virtual_key_code)echo "up: ", virtual_key_codeupdate_keyboard( the_wain_key, ord Release)a_waindow[].relays.key_relay(a_waindow[], the_wain_key.key, ord Release)of WM_KEYDOWN, WM_SYSKEYDOWN:
echo virtual_key_code, " <> ", scan_code
echo "down: ", virtual_key_codevar the_wain_key = wain_keyboard_key_from_windows_virtual_key_code(virtual_key_code)update_keyboard( the_wain_key, ord Press)a_waindow[].relays.key_relay( a_waindow[], the_wain_key.key, Press.ord)