diff --git a/.github/workflows/deb-build.yml b/.github/workflows/deb-build.yml new file mode 100644 index 0000000..b659122 --- /dev/null +++ b/.github/workflows/deb-build.yml @@ -0,0 +1,92 @@ +name: Build and Package + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + outputs: + deb_file: ${{ steps.package.outputs.deb_file }} + version: ${{ steps.package.outputs.version }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up CMake + uses: jwlawson/actions-setup-cmake@v2.0.2 + with: + cmake-version: '3.23' + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y libx11-dev libxkbfile-dev dpkg-dev fakeroot + + - name: Verify CMake version + run: cmake --version + + - name: Build project + run: | + mkdir -p build + cd build + cmake .. + make + + - name: Package project + id: package + run: | + cd build + cmake --build . --target create_deb + DEB_FILE=$(ls *.deb) + VERSION=$(echo $DEB_FILE | grep -oP '\d+\.\d+\.\d+') + echo "DEB_FILE=${DEB_FILE}" >> "$GITHUB_OUTPUT" + echo "VERSION=${VERSION}" >> "$GITHUB_OUTPUT" + + - name: Upload .deb package as artifact + uses: actions/upload-artifact@v4 + with: + name: xkb-switch-deb + path: build/*.deb + + publish: + needs: build + runs-on: ubuntu-latest + if: github.event_name == 'push' + + steps: + - name: Download .deb package + uses: actions/download-artifact@v4 + with: + name: xkb-switch-deb + + - name: Create GitHub Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ needs.build.outputs.version }} + with: + tag_name: "v${{ env.VERSION }}.${{ github.run_number }}" # Example of using a pseudo semantic versioning + release_name: "Version ${{ env.VERSION }}, pipeline run ${{ github.run_number }}" + draft: true # Ensure the release is published + prerelease: false # Change to false if you do not want it as a prerelease + + - name: Upload Release Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DEB_FILE: ${{ needs.build.outputs.deb_file }} + VERSION: ${{ needs.build.outputs.version }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./${{ env.DEB_FILE }} + asset_name: xkb-switch-${{ env.VERSION }}.${{ github.run_number }}.deb + asset_content_type: application/octet-stream + diff --git a/CMakeLists.txt b/CMakeLists.txt index 1756cd7..5424fff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +CMAKE_MINIMUM_REQUIRED(VERSION 3.10) PROJECT( XKB-SWITCH ) SET(MAJOR_VERSION 2) -SET(MINOR_VERSION 0) +SET(MINOR_VERSION 1) SET(RELEASE_VERSION 1) SET(XKBSWITCH_VERSION ${MAJOR_VERSION}.${MINOR_VERSION}.${RELEASE_VERSION}) ADD_DEFINITIONS(-DXKBSWITCH_VERSION="${XKBSWITCH_VERSION}") @@ -84,3 +84,25 @@ endfunction() # Compress and install man page install_man(xkb-switch 1) + +# Set CPack variables +SET(CPACK_PACKAGE_NAME "xkb-switch") +SET(CPACK_PACKAGE_VERSION ${XKBSWITCH_VERSION}) +SET(CPACK_PACKAGE_CONTACT "Sergei Mironov ") + +# Set Debian-specific variables +SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Sergey Korablin ") # required +SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.2.5)") # update as needed +SET(CPACK_DEBIAN_PACKAGE_SECTION "utils") # update as needed +SET(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) + +# Include CPack +include(CPack) + +# Custom target to run CPack +ADD_CUSTOM_TARGET(create_deb + COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} cpack -G DEB + DEPENDS xkb-switch + COMMENT "Packaging the project to .deb with CPack" +) + diff --git a/README.md b/README.md index 8d76f18..ccd1211 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ Usage: xkb-switch -s ARG Sets current layout group to ARG xkb-switch -W Infinitely waits for group change xkb-switch -n|--next Switch to the next layout group xkb-switch [-p] Displays current layout group + xkb-switch -f|--fancy Displays fancy name of current layout group ``` *A note on `xkb-switch -x`* diff --git a/man/xkb-switch.1 b/man/xkb-switch.1 index f55e2bf..d97aec6 100644 --- a/man/xkb-switch.1 +++ b/man/xkb-switch.1 @@ -1,4 +1,4 @@ -.TH "xkb-switch" "1" "1.3.1" "J. Bromley, S. Mironov, Alexei Rad'kov" "User Commands" +.TH "xkb-switch" "1" "2.1.1" "J. Bromley, S. Mironov, Alexei Rad'kov" "User Commands" .SH "xkb-switch" .LP .B xkb\-switch @@ -39,6 +39,9 @@ Switch to the next layout group. .BR \-p Display current layout group. .TP +.TP +.BR \-f ", " \-\^\-fancy +Display fancy name of current layout group. .SH "AUTHORS" .LP J. Bromley, S. Mironov, Alexei Rad'kov diff --git a/src/XKbSwitch.cpp b/src/XKbSwitch.cpp index 15b7393..98a428b 100644 --- a/src/XKbSwitch.cpp +++ b/src/XKbSwitch.cpp @@ -47,6 +47,7 @@ void usage() cerr << " xkb-switch -n|--next Switch to the next layout group" << endl; cerr << " xkb-switch -d|--debug Print debug information" << endl; cerr << " xkb-switch [-p] Displays current layout group" << endl; + cerr << " xkb-switch -f|--fancy Displays fancy name of current layout group" << endl; } string print_layouts(const string_vector& sv) @@ -77,6 +78,7 @@ int main( int argc, char* argv[] ) int m_print = 0; int m_next = 0; int m_list = 0; + int m_fancy = 0; int opt; int option_index = 0; string newgrp; @@ -91,9 +93,10 @@ int main( int argc, char* argv[] ) {"next", no_argument, NULL, 'n'}, {"help", no_argument, NULL, 'h'}, {"debug", no_argument, NULL, 'd'}, + {"fancy", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0}, }; - while ((opt = getopt_long(argc, argv, "s:lvwWpnhd", + while ((opt = getopt_long(argc, argv, "s:lvwWpnhdf", long_options, &option_index))!=-1) { switch (opt) { case 's': @@ -131,6 +134,9 @@ int main( int argc, char* argv[] ) case 'd': verbose++; break; + case 'f': + m_fancy++; + break; case '?': THROW_MSG(verbose, "Invalid arguments. Check --help."); break; @@ -191,7 +197,12 @@ int main( int argc, char* argv[] ) } if(m_print) { + if (!m_fancy) { cout << syms.at(xkb.get_group()) << endl; + } + else { + cout << xkb.get_long_group_name() << endl; + } } if(m_list) { diff --git a/src/XKeyboard.cpp b/src/XKeyboard.cpp index a0fc3d9..64782f3 100644 --- a/src/XKeyboard.cpp +++ b/src/XKeyboard.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2008 by Jay Bromley - * Copyright (C) 2010-2023 by Sergei Mironov + * Copyright (C) 2010-2024 by Sergei Mironov * * This file is part of Xkb-switch. * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -112,6 +112,32 @@ struct XkbRF_VarDefsRec_wrapper { } }; +struct XkbKeyboardWrapper { + XkbDescPtr ptr; + XkbKeyboardWrapper(XkbDescPtr p) : ptr(p) {} + ~XkbKeyboardWrapper() { + if (ptr) { + + XkbFreeKeyboard(ptr, 0, True); + } + } + // Disable copying + XkbKeyboardWrapper(const XkbKeyboardWrapper&) = delete; + XkbKeyboardWrapper& operator=(const XkbKeyboardWrapper&) = delete; +}; + +struct XGetAtomNameWrapper { + Display* display; + char* ptr; + XGetAtomNameWrapper(Display* d, Atom atom) + : display(d), ptr(XGetAtomName(d, atom)) {} + ~XGetAtomNameWrapper() { + if (ptr) { + XFree(ptr); + } + } +}; + layout_variant_strings XKeyboard::get_layout_variant() { XkbRF_VarDefsRec_wrapper vdr; @@ -196,6 +222,50 @@ int XKeyboard::get_group() const return static_cast(xkbState.group); } +std::string XKeyboard::get_long_group_name() const { + if (_display == nullptr) { + throw std::runtime_error("Display not opened."); + } + + XkbStateRec xkbState; + + if (XkbGetState(_display, _deviceId, &xkbState) != Success) { + throw std::runtime_error("Failed to get keyboard state."); + } + + XkbDescPtr descPtr = XkbGetKeyboard(_display, XkbAllComponentsMask, _deviceId); + if (descPtr == nullptr) { + throw std::runtime_error("Failed to get keyboard description."); + } + + XkbKeyboardWrapper desc(descPtr); + + if (desc.ptr->names == nullptr) { + throw std::runtime_error("Failed to get keyboard names."); + } + + Status ErrorGetControls = XkbGetControls(_display, XkbAllComponentsMask, desc.ptr); + if (ErrorGetControls != Success || desc.ptr->ctrls == nullptr) { + throw std::runtime_error("Failed to get keyboard controls."); + } + + int num_groups = desc.ptr->ctrls->num_groups; + if (xkbState.group >= num_groups) { + throw std::runtime_error("Group index out of range."); + } + + XGetAtomNameWrapper groupName(_display, desc.ptr->names->groups[xkbState.group]); + if (groupName.ptr == nullptr) { + throw std::runtime_error("Failed to get group name."); + } + + std::string longGroupName = groupName.ptr; + + return longGroupName; +} + + + // returns true if symbol is ok bool filter(const string_vector& nonsyms, const std::string& symbol) { diff --git a/src/XKeyboard.hpp b/src/XKeyboard.hpp index 821857b..df1b5e5 100644 --- a/src/XKeyboard.hpp +++ b/src/XKeyboard.hpp @@ -69,6 +69,9 @@ class XKeyboard void build_layout_from(string_vector& vec, const layout_variant_strings& lv); void build_layout(string_vector& vec); + // Returns fancy layout name as a string + std::string get_long_group_name() const; + // Waits for kb event void wait_event(); };