Linux Mint + Razer Blade = no sound

Another laptop, another Linux distro, same old issues… this time the solution was found in an obscure Bugzilla report in the Kernel.org website.
In comment 94 a user published a script that’s completely voodoo to me, but (thanks to another post on reddit or something) I could make it work. To not forget (and maybe help some other poor soul, here’s what to do.
Install alsa-tools ( sudo apt install alas-tools )
Then download the script from the comment. You then have to open it in a text editor of your choice, and you will need to replace all the hwC2D0 names by the correct one for you. In my case (and apparently in the case of most Razer Blade laptops) it’s hwC1D0
Then run the script ( sh RB14_2023_enable_internal_speakers_ver2.sh )

If you want to make sure hwC1D0 is correct for you, you need to run the following command in a terminal: aplay -l It should output something like this:

**** List of PLAYBACK Hardware Devices ****
card 0: NVidia [HDA NVidia], device 3: HDMI 0 [HDMI 0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: NVidia [HDA NVidia], device 7: HDMI 1 [HDMI 1]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: NVidia [HDA NVidia], device 8: HDMI 2 [HDMI 2]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: NVidia [HDA NVidia], device 9: HDMI 3 [HDMI 3]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: sofhdadsp [sof-hda-dsp], device 0: HDA Analog (*) []
  Subdevices: 0/1
  Subdevice #0: subdevice #0
card 1: sofhdadsp [sof-hda-dsp], device 3: HDMI1 (*) []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: sofhdadsp [sof-hda-dsp], device 4: HDMI2 (*) []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: sofhdadsp [sof-hda-dsp], device 5: HDMI3 (*) []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: sofhdadsp [sof-hda-dsp], device 31: HDA Analog Deep Buffer (*) []
  Subdevices: 1/1
  Subdevice #0: subdevice #0

You want to locate the line that has HDA Analog in it. This line contains a card number and a device number, and this gives you the correct name: hw and C followed by the card number (1 in the example above), and D followed by the device number (0 in the example above) which gives hwC1D0.

And here is the script (renamed to .txt because WordPress is a piece of shit that won’t let me upload a .sh file…) in case the link to it dies in the futur (already edited with the correct name)

Windows Checklist

New Windows, same old crap to configure (and new ones every version…)

Install with a local account

At the step where it asks you to enter a Microsoft account or create one, you can no longer skip… Fuck you Microsoft… even Apple still lets you its OS without forcing you to create a fucking iCloud account…

Anyway. Just use no@thankyou.com and enter some random password: it will say something along the line of “Whoops, too many attempts with incorrect password” and will let you create an offline account…

Reduce the number of running services

Regedit, HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control. In there, locate or create the DWORD value SvcHostSplitThresholdInKB and set the value to 4000000.

Stop pushing Bing crap on me when I’m searching for an app

HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\Explorer (created the last key if needed)
New DWORD value: DisableSearchBoxSuggestions = 1

Stop writting System Volume Information on removable drives

HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Windows Search (create the last key if needed)
New DWORD value: DisableRemovableDriveIndexing = 1
Disable Storage Service in services.

Customization Tools

List of tools that allow you to tweak Windows to remove some of its bloat, restore it to a functional desktop, etc.

Dumb things to do in C: named arguments!

Question: can the following be valid C (in the same compilation unit):

draw_text( .x = 10, .text = "Hello Sailor" );
draw_text( .text = "Hello World" );
draw_text( .text = "Hello Pirate", .color = 0xff0000ff );

Yup. Up until very recently I didn’t know you could kinda use named arguments in C, but you can. Of course, like most C thing, it involves a few stupid tricks, but still, the end result is pretty nice.

Note: I didn’t come up with this by myself, but I can’t remember where I saw that first. If I do, I’ll update this and add the source.

The following piece of code illustrate how you could do that:

// This struct holds all the options that our function will need.
typedef struct {
    int x, y;
    const char *text;
    unsigned int color;
} DrawTextOpt;

// Our draw_text function implementation.
void draw_text_impl(DrawTextOpt opt) {
    // draw you text with the given color, at the specified location.
}

// And here is the trick (warning: it's uggly :D)
#define draw_text(...)           \
    draw_text_impl(              \
        (DrawTextOpt){           \
            .x = 0,              \
            .y = 0,              \
            .text = "",          \
            .color = 0xffffffff, \
            __VA_ARGS__          \
        }                        \
    )

Uggly right? But still, cool! You get default options, and you can just override the ones you need using named arguments! Worth noting: this will trigger warnings on (probably) most compilers because we might override initializers (like text is initialized to "" and will mostly like be replace in the var args because not many people want to draw empty strings)

It’s pretty easy to fix, at the expanse of a little more uggliness :p

// Those macros temporarily disable the warning that's triggered. Clang's provided
// here for example, Gcc and other compilers will probalby have their own way of doing it.
#ifdef __clang__
#define NAMED_ARGS_BEGIN             \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Winitializer-overrides\"")
#define NAMED_ARGS_END \
    _Pragma("clang diagnostic pop")
#endif

// Here's the updated version of draw_text which no longer issue any warnings:
#define draw_text(...)           \
    NAMED_ARGS_BEGIN             \
    draw_text_impl(              \
        (DrawTextOpt){           \
            .x = 0,              \
            .y = 0,              \
            .text = "",          \
            .color = 0xffffffff, \
            __VA_ARGS__          \
        }                        \
    )                            \
    NAMED_ARGS_END

It could probably be further modified to make it easier to define new functions without having to rewrite most of the boilerplate C macros, but I’ll leave that to whomever (if any) ‘s reading this post.

Don’t use VcVarsAll/VsDevCmd

Long story shorts, those “helpers” provided by Visual Studio to setup your development environment are horribly slow, and seems to get slower on every new version. So, if you’re like me and don’t like to wait 2 to 10 seconds every single time you need a working environment in which you can call cl, then the following might interest you.

Those scripts are “helpers” which will ultimately invoke the one which actually setups the environment so that you can invoke the compiler from the command line, but the thing is they run a lot of other scripts which have nothing to do which this (if you’re curious, see the vsdevcmd\ext folder’s content, everything in it is executed…)

So to “speed things up” (still slow for something that just sets a few environment variables…) you can directly invoke the extension that only sets the Visual C(++) compiler in that folder (vcvars.bat) And since it’s a pain in the ass because it’s not meant to be called as a standalone, here is the minimum amount of boilerplate that I had to write to be able to call it:

@echo off

:: Configure for x64
set VSCMD_ARG_HOST_ARCH=x64
set VSCMD_ARG_TGT_ARCH=x64
set VSCMD_ARG_APP_PLAT=Desktop

:: The version of visual studio you're using (needed for the following scripts to work)
:: Fun fact: if you remove the trailing slash, things stop working. Nice, right?
set VSINSTALLDIR=C:\Program Files\Microsoft Visual Studio\2022\Community\
:: If you're using the Visual Studio Build Tools instead of the IDE:
:: set VSINSTALLDIR=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\

:: Setup the MSVC compiler and the Windows SDK
call "%VSINSTALLDIR%\Common7\Tools\vsdevcmd\ext\vcvars.bat"
call "%VSINSTALLDIR%\Common7\Tools\vsdevcmd\core\winsdk.bat"

:: The previous scripts in the past would correctly set the INCLUDE env var. But at some point, they
:: stopped doing so (somewhere between 17.2 and 17.3...) That env var is now set by the main script
:: which the whole point of this is to avoid calling. So here we are... until it breaks again...
if not defined INCLUDE set INCLUDE=%__VSCMD_VCVARS_INCLUDE%%__VSCMD_WINSDK_INCLUDE%%__VSCMD_NETFX_INCLUDE%%INCLUDE%

On my machine, calling this takes approximately 150ms, while calling the usual VsDevCmd.bat takes more than 2 seconds…

Kubuntu install checklist

Since every single fucking time I dare update Kubuntu, some things suddenly break down (sound, various drivers, Bluetooth, whatever) I end re-installing from scratch. Most of the time it works out of the box, but sometimes, things just don’t work, like the application launcher no longer opens when I press the Meta key or whatever.

So here is a checklist of things that can (and will, Linux being Linux) go wrong and how I solved it. It will save me some time googling on some obscure forums trying to figure out how to fix things that should not be broken in the first place.

Application launcher no longer opens on Meta (Windows key)

Edit ~/.config/kwinrc and add the following:

[ModifierOnlyShortcuts]
Alt=
Control=
Meta=org.kde.plasmashell,/PlasmaShell,org.kde.PlasmaShell,activateLauncherMenu
Shift=

After that, reconfigure: qdbus org.kde.KWin /KWin reconfigure

Edit: Ooouuh, I just found out that for this to work at all, the application launcher should have a shortcut assigned! Right-click on the application launcher’s icon, then select Configure Application Launcher..., then go to Keyboard Shortcut: if you remove the shortcut there, your windows key magically stops working… And if you put back any shortcut, then it works again… What fucking non-sense is that… (and I found this because always remap the Alt+FN shortcuts to workspace navigation, and by default Alt+F1 is assigned to the application launcher… So as soon as I did that my menu stopped opening -_-

Login screen resolution

If like me you have a laptop with a 15″ screen and a stupidly huge resolution, you will want to customize the resolution. Now, doing this in your KDE session is easy, but for some unfathomable reason the login screen cannot be configured through any UI…

Once again, you’re left opening a terminal and tweak yet another obscure configuration file. The following is the one I use on my laptop. Check xrandr and SDDM to find a ton of varying info on how to customize this.

The file is /usr/share/sddm/scripts/Xsetup and yes, you’ll need to be root to edit it… And I add the following to it:

xrandr --output eDP-1-1 --mode 1920x1080 --rate 60

Edit: nope, doesn’t work. Need to waste yet another hour to learn some fucking tool that I should not even care about… When did tech stop being about making our life easier, and instead became a pain in this ass making our life this miserable…

Get rid of snap crap…

The new “thing” in mainstream Linux distros nowadays seem to try to become as much of a bloatware as Windows… and thus snap and other flatpak craps were born… sure, it’s easier for devs, but at what cost?? Services running and hogging resources on every single user’s computer for no reason, software which are 10 times as fat as they used to be, slower to launch, etc.

So here we go again:

  1. snap list and then from its result, sudo snap remove --purge <package(s)>. The order is kinda important be this piece of crap software is not able to remove things in dependency order, so if you remove everything in one command line call, it’ll tell you “I’m dumb, I can’t do what you’re asking for.” The last 3 things to remove should be bare, core20 and snapd.
  2. sudo apt remove --autoremove snapd to remove that crap.
  3. To prevent this crap to install itself again and again and again, edit (in sudo mode) /etc/apt/preferences.d/nosnap.pref and add the following:
Package: snapd
Pin: release a=*
Pin-Priority: -10
  1. Reinstall Mozilla from deb: sudo add-apt-repository ppa:mozillateam/ppa and the usual install (check that the name of the packages contain deb, before accepting)
  2. And prevent apt from suggesting the snap updates… edit in sudo `/etc/apt/preferences.d/mozilla-firefox` and make it:
Package: *
Pin: release o=LP-PPA-mozillateam
Pin-Priority: 1001

Note that for the Mozilla thing, the name of the file to edit and the last part of the second line (after the o=) seem to depend on which source you re-installed Mozilla from (step 4)

Swig typemaps cheatsheet

The documentation of SWIG is pretty extensive, but there isn’t a single point that document all available macros / substitution mechanisms available while writing a typemap. It’s a nightmare to find what you’re looking for when you’re not already really familiar with it, so to save myself and maybe a few others from early hair loss, here is a quick list of some useful substitutions I found.

Assuming I’m writing an out typemap for a template<class T> class Handle;

$1The local copy of the C/C++ object
$resultThe output PyObject to return to Python
$1_typeThe type of the C/C++ object (Handle<T>)
$1_descriptor The SWIG type descriptor corresponding to Handle<T>
$&1_descriptor The SWIG type descriptor corresponding to Handle<T> *

The case I was trying to cover when I almost became bald is the following: I have a templated Handle class (as you’ve probably already guessed) that’s used to encapsulate a heap allocated data. Some API functions / methods bound in Python return such handles in place of pointers, but I wanted the those to return None in Python instead of returning an invalid handle.

So I basically needed a typemap that checked the validity of my handle, and if invalid returned None, otherwise returned the usual SWIG object. Here is the simplified typemap:

%typemap(out) Handle {
    if ($1.valid() == false) {
        $result = Py_None;
    } else {
        // this fucking $&1_descriptor is at the origin of this post...
        $result = SWIG_NewPointerObj(new $type(std::move($1)), $&1_descriptor, SWIG_POINTER_OWN);
    }
}

Linking against a static Qt5 build using CMake

The previous post was the script I use to statically build Qt5 on Windows, but the real fun part is then linking an application using that version ! Here is the part of the CMake script I’m using (as usual, for reference)

For the moment I only build a basic Widgets application. As I use more modules / functionalities, I’ll update the script. Also please note that the very beginning (finding Qt) is tailored to my development setups, so you might need to update the paths (or provide CMake with a valid QT_DIR option)

Edit 2019-10-17: Since I removed accessibility from my build script, WindowsUIAutomationSupport is no longer needed as a dependency of the Windows integration plugin.

Edit 2019-10-18: I created a repository to start collecting my various CMake helper scripts, and the following script is now hosted there (it’s also a lot cleaner, modular and easy to use) You can see it here: https://github.com/dcourtois/CMakeUtils/blob/master/Qt.cmake

#
# Set to On to use a statically built version of Qt
#
set (QT_STATIC On)
if (QT_STATIC)
    set (QT_STATIC_SUFFIX "static/lib/cmake")
endif ()

#
# Find Qt.
#
find_path (QT_DIR Qt5
    HINTS
        # User set directory
        "${QT_DIR}"

        # My dev directories, depending on which machine I'm on
        "C:/Development/Libs/Qt/5.13.1"
        "D:/Development/Libs/Qt/5.13.1"
        "~/Development/Libs/Qt/5.13.1"

    PATH_SUFFIXES
        # As for the dev dirs, these are the suffixes to the CMake folder on the various machines I own.
        # Note that those are the default subfolder when installing Qt from its official installer.
        "${QT_STATIC_SUFFIX}"
        "msvc2017_64/lib/cmake"
        "gcc_64/lib/cmake"
        "clang_64/lib/cmake"
)

#
# Log / Error
#
if (NOT QT_DIR)
    message (FATAL_ERROR "Couldn't find Qt. Use QT_DIR variable to point to a valid Qt install.")
else ()
    message (STATUS "Found Qt in '${QT_DIR}'")
endif ()

#
# Find our Qt components
#
set (CMAKE_PREFIX_PATH ${QT_DIR})
find_package (Qt5 5
    COMPONENTS
        Widgets
    REQUIRED
)

#
# When using static build, exported Qt targets miss a awefull lot of dependencies (on Windows
# at least, didn't check the other platforms) so to avoid bothering, patch Qt5::Widgets
#
if (QT_STATIC)

    #
    # Set a few paths
    #
    set (QT_LIB_DIR "${QT_DIR}/..")
    set (QT_PLUGIN_DIR "${QT_DIR}/../../plugins")

    #
    # Qt5::QWindowsIntegrationPlugin
    # note that this target somehow is already there even if we didn't search for it in the
    # find_package command. And since it's mandatory to run the most basic Widgets application
    # on Windows ...
    #
    if (TARGET Qt5::QWindowsIntegrationPlugin)

        # find additional components needed by the windows platform plugin
        find_package (Qt5 5
            COMPONENTS
                EventDispatcherSupport
                FontDatabaseSupport
                ThemeSupport
            REQUIRED
        )

        # configure direct dependencies of the plugin
        target_link_libraries(Qt5::QWindowsIntegrationPlugin
            INTERFACE
                # Qt targets
                Qt5::EventDispatcherSupport
                Qt5::FontDatabaseSupport
                Qt5::ThemeSupport

                # Windows libs
                Dwmapi.lib
                Imm32.lib
                Wtsapi32.lib

                # The following is needed if you want to use the Windows vista style plugin.
                # If you provide your own style or CSS, you can comment out the following libs.
                ${QT_PLUGIN_DIR}/styles/qwindowsvistastyle.lib
                UxTheme.lib
        )

    endif ()

    #
    # Qt5::FontDatabaseSupport
    #
    if (TARGET Qt5::FontDatabaseSupport)

        target_link_libraries(Qt5::FontDatabaseSupport
            INTERFACE
                # Qt libs
                ${QT_LIB_DIR}/qtfreetype.lib
        )

    endif ()

    #
    # Qt5::Gui
    #
    if (TARGET Qt5::Gui)

        target_link_libraries(Qt5::Gui
            INTERFACE
                # Qt targets
                Qt5::QWindowsIntegrationPlugin

                # Qt libs
                ${QT_LIB_DIR}/qtlibpng.lib
        )

    endif ()

    #
    # Qt5::Core
    #
    if (TARGET Qt5::Core)

        target_link_libraries(Qt5::Core
            INTERFACE
                # Qt libs
                ${QT_LIB_DIR}/qtpcre2.lib

                # Windows libs
                Netapi32.lib
                Ws2_32.lib
                UserEnv.lib
                Version.lib
                Winmm.lib
        )

        target_compile_definitions (Qt5::Core
            INTERFACE
                # Remove debug stuff from Qt
                $<$<CONFIG:Release>:QT_NO_DEBUG>
                $<$<CONFIG:Release>:QT_NO_DEBUG_OUTPUT>
                $<$<CONFIG:Release>:QT_NO_INFO_OUTPUT>
                $<$<CONFIG:Release>:QT_NO_WARNING_OUTPUT>

                # Since Qt was built in release, we need to match it on Windows
                _ITERATOR_DEBUG_LEVEL=0
        )

    endif ()

endif ()

So this needs to be used after your project has been defined, and then you’ll be able the just add Qt5::Widgets to your target’s link libraries, and it should correctly link everything needed to run your Widgets application.

You’ll still need to manually import plugins though, and since the platform integration one is mandatory, here is how to do it (put that in your main compilation unit or wherever you want)

#if defined(QT_STATIC)

// needed to import statically linked plugins
#include <QtPlugin>

// The minimum plugin needed is the platform integration. Without it, the application just crashes on startup.
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);

// This one is for the Windows 'Vista' theme. As I wrote in the CMake part, this is
// optional if you plan on using your own CSS or style.
Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin);

#endif

Qt5 static Windows build script

To avoid loosing the script I use to build Qt 5 on Windows, here is the latest version I’m using. Note that I’m compiling using Visual Studio 2019 and I disable things I don’t need to try to minimize build time.

Edit 2019-10-17: Removed accessibility and harfbuzz

@echo off

::
:: Configure Visual Studio 2019 Community for x64 build.
::
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64

::
:: Set Qt directory and set it as the current one.
::
set QT_DIR=C:\Development\Libs\Qt\5.13.1
pushd "%QT_DIR%\Src"

::
:: Configure Qt
::
call configure -release             ^
    -static                         ^
    -opensource -confirm-license    ^
    -prefix "%QT_DIR%/static"       ^
    -platform win32-msvc            ^
    -mp                             ^
    -c++std c++17                   ^
    -no-accessibility               ^
    -no-icu                         ^
    -no-harfbuzz                    ^
    -nomake examples                ^
    -nomake tests                   ^
    -skip qtwebengine               ^
    -skip qt3d

::
:: Build and install
::
nmake
nmake install

::
:: Restore the previous working directory
::
popd

Overwrite all git commit’s author

As a little post-it, here’s a script to amend author’s name and email of all commits of a repository. Use with care, it will override all commits, no matter what the original author is.

#!/bin/sh

if [ "$#" -ne 2 ]; then
	echo "usage: amend 'name' 'email'"
	exit 1
fi

git filter-branch -f --env-filter "
	GIT_AUTHOR_NAME='$1'
	GIT_AUTHOR_EMAIL='$2'
	GIT_COMMITTER_NAME='$1'
	GIT_COMMITTER_EMAIL='$2'
" HEAD

Credit: https://stackoverflow.com/a/750191/913135

Visual Studio 2017, CMake and launch settings

Long story short, documentation for CMake related features in Visual Studio 2017 sucks. It’s scattered among a ton of blog post where most of the code samples are outdated or contain errors. Here is an example launch.vs.json that I’m using for reference:

{
	"version": "0.2.1",
	"defaults": {},
	"configurations": [
		{
			"type": "Debug",
			"project": "CMakeLists.txt",
			"projectTarget": "foo.exe",
			"name": "foo.exe",

			// currentDir and not cwd like we can read in most examples ...
			"currentDir": "${workspaceRoot}",

			// this one is pretty straightforward
			"args": [ "arg0", "arg1" ],

			// env overwrites all the environment variables ...
			// separate vars by a null (\u0000) character. You can use existing env vars, see PATH
			"env": "TEST_ENV=foo\u0000HELLO=World\u0000PATH=${env.PATH};${workspaceRoot}"
		}
	]
}