My first 100 days with a MacBook Pro 16″ impression: MacOS vs KDE/Linux comparison

During autumn/winter 2019, the Slimbook Katana I had started showing symptoms(*) that I would’ve been better off changing laptop, soon. Therefore I started looking for viable alternatives and you can imagine my surprise when Apple announced, during that very same period, that a new MBP was about to be released. With the promise of finally fixing all the poor engineering decisions (i.e. the keyboard) made in the past few generations of MackBooks, I said to myself “Well, this could finally be the time I’ll buy one of these machines”.

So I waited for about a month, reading and watching online reviews to get a better idea about it, until I finally settled on the decision; and since mid-January 2020, after 16 years of day-to-day Linux usage, I moved to Apple, at least on the personal side.

Note: I’m not going to repeat what countless reviews already said about how good the product is; it truly is the best hardware I’ve had the pleasure to play with. I’ll point out instead what I, as a KDE/Linux user, would have expected to see/have in the software department, given how much appraisal MacBooks receive from their regular userbase.

The point of this blog post is to give a more down-to-earth MacBook Pro review, especially for people who’ve never used an Apple product before and don’t know what it would be like. And perhaps a laugh or two 🙂 It would also be nice to hear how your experience was, so don’t hesitate to drop a comment or leave a remark/suggestion in case I made a mistake/missed something.

And in case you’re curious about my setup, this picture should give you all the infos you’d ever need to know.

also, an AMD Radeon Pro 5500M, 8GB GDDR6

Intro

For many years, I’ve been hearing praises from Mac users, repeatedly saying how Apple is the pinnacle of User Experience, Usability, and integration with other Apple products. While I can’t say anything about the latter, since this laptop is the only Apple device in my possession, I can say a thing or two about UX and software in general, which hopefully will help other people who’s considering to buy a MBP.

 

Finder

The very first thing you’ll notice in your day-to-day usage is that Finder, the Dolphin/Nautilus/Explorer equivalent for KDE/Gnome/Windows, has a very peculiar way to handle navigation: if you hit Enter, you won’t go inside a folder/open a file with its associated app. Instead, it will highlight the filename, such that you can rename it. And backspace won’t bring you back to the previous folder, in case you were wondering.

While there is a very good argument about the real meaning of using Enter key, it certainly does not explain why doing so should mean “I want to rename a file”. And if we want to follow the same line of reasoning, it certainly won’t explain then why hitting the spacebar is going to actually play a video (mind you, only if its format is recognized by QuickTime), open a simple image editor if it’s an image, or just display a popover with a tiny bit of infos (size and last modified) in the other cases. In what universe are these sensible, UX compliant behaviors? Luckily this ~6 years old question has still a valid solution for it, that is, install an extra  software to configure Finder’s behavior.

Also, by default, Finder doesn’t impose any layout on how files are visually lay out, which means, you’ll end up in a mess of misplaced icons sitting all around your desktop and folders. Luckily you can set the sorting option to “Align to Grid” and boom, and you’ll have neatly positioned files. Again, though, in what universe is “align to grid” a valid sorting option? And why this option is not active by default anyway?

In my day to day work and life, I happen to copy text, snippets of code, etc from various sources like Slack, github, or even chats and save it on disk, such that I can read it later even when I have no internet connection. How would you do that? You’d copy some text, then right click on the desktop/empty space inside a folder and select “New F”… oh wait, there is no such option, how peculiar. Once again, AskDifferent to the rescue:

If you have the Finder window open, use Spotlight to open TextEdit. When you’re ready to save the file, option+drag the text file icon from the title bar of TextEdit into the Finder window where you want to save it.

Or, if you think this solution is a bit.. clumsy, you can create an Automator script, and assign a keyboard shortcut to it.

With KDE? Copy the text and paste into the destination folder, then give it a name. Just as simple as that.

Example in action; it would have been even faster if I CTRL-C and CTRL-V, but visually you wouldn’t have seen a lot going on 🙂

 

Window Management

I always wondered why my colleagues using Apple MBPs rarely do maximize their windows; I’d personally want to have them fully maximized (especially when coding, using the console or browsing the internet), right? Sure… until you discover how annoying MacOS window manager is. First of all, the button that you would think maximizes the window, will actually set your application in fullscreen mode, which has the neat effect to place it on a new, separate virtual desktop. One fullscreen app = one virtual desktop. And secondly: in this state you cannot have, say, a non-maximized application over a fullscreen one, so you’ll see a constant sliding animation transition between apps which will, very soon, start to annoy you. At this point you have the following options:

Do you like to tile windows, from time to time? You’ll have to have a sniper-perfect aim, hover your pointer over your app’s fullscreen button, wait until a popover will show up, then choose whether you want to tile your window to the left, or to the right.

Would you like to have more tiling options, or perhaps window snapping? You can’t. Unless you install a third-party app, that is.

On the other hand, this is how it’s done with KDE:

Just simple, neat and immediate.

 

Clock applet

How difficult would it be turning a not-so-useful clock applet like this

into something more that conveys more info, say a small calendar? For example, here’s KDE’s clock applet

And how cool would it be, if I could also check the vacation days? Even better, if I could pick any number of Countries, such that you could see their vacation days at a glance? Well, KDE has been doing this for years as well:

Yes, I know, there’s a Calendar app that can be configured to eventually do sort of the same (but still, no clock <-> calendar events synchronization/visualization, as far as I know).. but why should I use an app, when the clock applet is sitting there, at one mouse click distance from me, doing nothing?

 

OS Updates

When I updated MacOS from 10.15.3 to 10.15.4, I didn’t notice I was still connected to internet using my phone (crappy day with my regular internet connection). Fair enough, I cancelled the process just after downloading ~40MB of data, with the intent to resume the process later in the week. But then, MacOS decided it was already fully upgraded to 10.15.4. I therefore tried to force-check for updates, which didn’t work. Downgrading MacOS wasn’t an option either in my case. In the end I found out that I could download the entire *.dmg update package from Apple website, and execute it locally. I did run it thrice, just in case 🙂 Well, I had to, because the first two attempts ended up prematurely due to unspecified errors… and when I was almost in despair, the third time did finally succeed.

I don’t recall ever having such experience when upgrading packages (once, when doing a major OS upgrade between two versions, not one) with Kubuntu, just sayin’. Network unexpectedly goes down? You can retry later, and apt will pickup from where it left.

 

Misc

 

TL;DR: so, is it worth it or not?

Yes. Despite me ranting for the reasons above (and rest assured, I’ll keep the list updated!), I want also to emphasize what I’ve said at the beginning of this post: it truly is the best hardware I’ve had the pleasure to play with, in a long time. Great screen, awesome cpu performance, extremely silent, audio quality beyond belief. Battery doesn’t last as it’s advertised, which shouldn’t come as a surprise at all. The trackpad and its gestures are great when you’re on the move and for mundane tasks, but for anything serious, in my opinion using a mouse is still the way to go. The preinstalled software is overall okay; I like garageband and the piano lessons it offers, but I didn’t have a chance to use the Office suite yet. The touchbar feels a bit of a gimmick, but for example I do love the autocorrection proposals it shows when I make a typo.

In the end, the MacBook Pro is a good laptop; I just wish Apple would have used more common sense in developing its apps, considering how an OpenSource community like KDE can deliver high quality apps despite having

  • far less developers at its disposal
  • much broader hardware pool to support

And perhaps, it would be great if Apple would add Linux support in the same way they support Windows with the Bootcamp project. I also wish Apple adopted KDE’s philosophy, “simple by default, powerful when needed“, rather than “weird by default, powerful if you google for an answer that usually leads to install 3rd-party apps to get what you need“.

But who knows, maybe in the future…

 

 

 

(*) to elaborate it a bit more: after a year of usage, one of the two USB ports stopped working. I had to ship it back to Spain, where they replaced the entire motherboard and shipped it back to me. The whole process took more than 5 weeks in total and, few months after the 2-year warranty expired, the very same USB port stopped working again. meh. On top of that, I started experiencing random startup failures, and pc not resuming from sleep anymore, even after a clean OS installation, and despite checks on the SSD/RAM showed no outstanding issues at all.

Make your own executable shared library on Linux

Hello there, it’s been a long time since my last post 🙂

Since I’m on vacation, and kinda frustrated about my playbook experience, I decided to  statisfy something that always tickled my fancy about shared libraries.

As you may know, under GNU/Linux is it possible to make shared libraries also executable: prominent examples are libc and Qt libraries, as shown in the screenshot below.

libc and libQtCore output

libc and libQtCore output

Those libraries, when executed from the command line, print useful information about the library version itself, the compiler used, processor features, author, copyright and so on .

But how they achieved that? Libraries don’t have an entry point .. or they do?

LD manual to the rescue

Of course the answer is yes, they do .. sort of. If you look at the options you can issue to ld, you will notice that there is the following option:

-e entry

--entry=entry  Use entry as the explicit symbol for beginning execution of your program, rather than the default entry point. […]

Bingo!

So what you will actually do is to define a function, from whithin your library, that will be used as entry point when executed. From there, you can execute your arbitrary piece of code.

The Code

We will start by defining a sample library composed of just one class (I kept everything as simple as possible to improve readabilty), which will do nothing but print “hello lib!”  upon construction:

/* lib/mysamplelib.h */
#ifndef SAMPLELIB_H
#define SAMPLELIB_H
namespace MySampleLib
{

class SampleLib
{

public:
    SampleLib();
};

} // namespace MySampleLib

#endif // SAMPLELIB_H

/* lib/mysamplelib.cpp */
#include "samplelib.h"
#include

namespace MySampleLib
{

SampleLib::SampleLib()
{
    std::cout << "hello lib!" << std::endl;
}

} // namespace MySampleLib

Then, we will define our custom entry point function in a separate file: this will print “Hello from custom entry point lib!”.

/* lib/dump_function.cpp */

#include
#include

extern "C" void my_dump_function() {
    printf("Hello from custom entry point lib!\n");
    exit(0);
}

We also need a very basic main.cpp file, which prints the usual “Hello World!” and then instantiate a SampleLib object:

/* main.cpp */

#include
#include "lib/samplelib.h"

int main()
{
    std::cout << "Hello World!" << std::endl;
    SampleLib lib = SampleLib();
    return 0;
}

For completeness, I will also include the two CMakeLists.txt files used in the project.

/* CMakeLists.txt */
project(executable_library)
cmake_minimum_required(VERSION 2.8)

add_subdirectory(lib)

set(SRC_LIST
    main.cpp
)

add_executable(${PROJECT_NAME} ${SRC_LIST})
target_link_libraries(${PROJECT_NAME} sampleLib)

/* lib/CMakeLists.txt */
set(LIB_SRC
    samplelib.cpp
    dump_function.cpp
)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -e my_dump_function")

add_library(SampleLib SHARED ${LIB_SRC})

And now we can finally build and run our app and our library and see the results 🙂

First Attempt

First Attempt

Urg .. What’s going on here?

Compilation and linkage were successful, the executable ran perfectly, but executing the library directly produced a segfault.

The Fix

Let’s try to find out what’s wrong here. We will run readelf to see the if there are some parts in common between the executable, our library, and some other executable library – i.e. libc – . These are the output:

readelf exec

readelf exec

readelf libc

readelf libc

And now, our libSampleLib

readelf samplelib

readelf samplelib

Nice, now we have much more infos about the issue. As you can see the executable, libc, and our SampleLib, all have  an entry point defined. That means that ld processed our command option as we intended.

But, as opposed to the executable and libc, our SampleLib does not have an interpreter section defined. This is normally good when a library gets executed through a regular application, because every application has an interpreter defined. In our case anyway, that missing definition is the reason of our segfault.

So we need to tell the compiler to add an interpreter header. From a quick search on google, all you need is to add the following line inside dump_funcion.c (outside the function body of course)

extern const char elf_interpreter[] __attribute__((section(".interp"))) = "/lib64/ld-linux-x86-64.so.2";

Recompile all, and now …

Second Attempt

Second Attempt

Awesome, we did it!

But we are serious programmer, aren’t we? Hardcoding the interpreter absolute path is always a Bad Thing™; if you work in a team where everyone may potentially have different setup and machines, that’s even worse.

CMake to the rescue!

With a little help of CMake, we can automate the interpeter path retrival.

Firs we execute readelf on a command that is present in every linux distro, which is “ls” ; then, with a little cmake regexp magic, we extract the path, and finally add a new cmake definition whose value will be assigned to the elf_interpreter[] var inside dump_function.cpp .

The updated CMake file and main.cpp look now like this:

/* lib/CMakeLists.txt */
set(INTERPRETER_DESCRIPTION "Requesting program interpreter:")

execute_process(COMMAND readelf -l /bin/ls
    RESULT_VARIABLE return_value
    OUTPUT_VARIABLE result
)

if(return_value)
    message(STATUS "Cannot find a valid ELF interpreter")
else()
    string(REGEX REPLACE
        ".*[[]${INTERPRETER_DESCRIPTION} ([/][^ ].+)[]].*" "\\1"
        _ELF_INTERPRETER_PATH "${result}"
    )

    add_definitions( -DELF_INTERPRETER_PATH="${_ELF_INTERPRETER_PATH}" )
    message(STATUS "ELF interpreter is ${_ELF_INTERPRETER_PATH}")
endif()
set(LIB_SRC
   samplelib.cpp
   dump_function.cpp
)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -e my_dump_function")
add_library(SampleLib SHARED ${LIB_SRC})

/* lib/dump_function.cpp */
/* showing only the relevant line here, too lazy to copy everythin again! */
extern const char elf_interpreter[] __attribute__((section(".interp"))) = ELF_INTERPRETER_PATH;

That’s better 🙂

Further improvements and how to get the code

You can find an improved version of the code shown here on my gitorious repository . I made some modifications to make it prettier and more reusable, such as define a FindELFInterpreter script for that purpose, added a simple macro to retrieve the hash commit/current branch of your source tree (in this case, mine), and embed that info within my_dump_function(). I’ve also added some debug compiler options, and added to the dump function, just to show some other interesting output you may be interested in seeing. Pretty neat, isn’t it? 🙂

Last Attempt

Last Attempt

An other interesting thing to implement could be use cmake feature to create header files through its configure_file() command, and put all of those definitions inside it and include that file whenever you need it, but I leave this as an exercise to the reader, cheers 😉