Git has this cursed function to fuck up your files by doing unspeakable things to your line endings.

For example, from Githubs Documentation on Line Endings:

On Windows, you simply pass true to the configuration. For example:

$ git config –global core.autocrlf true

Please, never never never never never never never never never never never never do this!

THERE IS NO REASON TO DO IT!

Git is here to keep track of our files, NOT TO CHANGE OUR FILES IN ANY WAY.

So, please, just, never never never never never never never never never never never never do this! Leave my file endings alone!

You might notice that the Shields.io badges, for the Nuget Packets I am maintaining, have been gone for some days and are now replaced by a simple table. What happened?

It happened with this decision of a German court that using Google Fonts from their CDN server hosted in the US can pose a privacy violation when used without user consent. Apparently, some predatorial lawyers are already on the hunt. Thus, I changed all my websites to host the fonts I am using in the same servers. While I was sort of angry about the extra effort I had to take, I understand the importance of data privacy, and, in that light, I can understand the decision. So, that is about the Google Fonts. What about anything else?

My goal was to change my website to either host everything myself, or to explicitly request content for any embedded content. And that is why Shields.io is no longer directly used on my website. Call me paranoid if you will, but I think this might be an improvement also on content “stability”. Now, my backend fetches all the data I need, store it at my own host, and delivers it as a local part of my web site. In this case, I even changed fetching the data to a cron job running once a day. I am not that fast with Nuget packing anyway.

With this, now all content displayed on sgrottel.de is delivered from the servers sgrottel.de is hosted on.

The downside, of course, is that this poses an additional maintenance burden on me. The backend is calling semi-documented Apis, which might change any time, and it does some fragile parsing, e.g., in case of the not so structured Lua project website. The current solution cannot be a final solution, and it will need to be improved in the future. We will see.

Okey, a warning up ahead: this is a venting post, because I am really upset and disappointed by my most recent experience with customer service by Lenovo.

Over a year ago, I bought a ThinkPad laptop with matching docking station. And the two never worked perfectly. Sometimes the docking stations would lose the signal to the attached monitor. Sometimes it would not switch on during boot, etc. All issues which could be fixed by either un-plugging and re-plugging or, in worse cases, by turning it off and on again.

Now, recently the docking station learned a new trick: it’s ethernet port started failing. First, sometimes, and then consistently always. The supposedly good thing is, the failure is reflected in the Windows Device Manager by one USB device reporting a startup error, and that at some point, it failed always, 100%. That’s something a customer service should be able to understand and fix, right. In worst case, even when they cannot reproduce the issue, they could see the error descripting and just replace the ethernet component, right. So, I decided to give it a try.

First contact, several weeks ago, worked as expected. I was asked to replace cables, reinstall clean software etc. All understandable. A Lenovo provided cable might be incompatible, it’s better to switch to something from Amazon. And after a clean Windows 10 reinstallation on a formatted hard drive reports exactly the same USB device error in the Device Manager as before, you might start believing it might really be a hardware issue.

So, at that point, the customer service agreed on me sending in the device to a service center. To my very surprise, they asked me to send in the laptop. Only the laptop. Not the docking station. Strange, but ok. I was prepared for a “long journey” anyway. So, I send in the laptop, together with a detailed description of what’s going on, and of how to reproduce the error. That is error description number one. That is important for the conclusion.

After roughly a week the laptop returns to me with a repair report in the package. I really do not understand why Lenovo has a customer service website tracking their report tickets. They provide absolutely no information on that website other that the tracking numbers of the postal services they use. It’s useless. Anyway. The repair report stated that they replace the mainboard and the ethernet port of the laptop. Funny. I clearly stated that the ethernet port of my docking station is acting up. Well, whatever. Interestingly, however, the occasionally issue with the monitor not being detected have disappeared since then. So, I think, there was something wrong with the mainboard. Ok.

But the issue with the ethernet port of my docking station is still broken. 100% of all the time. So, I create a Lenovo Customer Service Escalation to the unresolved issue. Round two started with me providing a detailed error description of the issue and of the steps to reproduce it (basically: connect laptop, boot, see error! Minimal setup with Laptop + Dock + 3 cables: laptop-dock connection, power supply, ethernet). After some more “experimentation” with software, I finally get to send in my device to the service center, again. To my surprise, Lenovo asked me to send in both the laptop and the docking station, as it might be an issue of the combination of both. Strange, but whatever, ok. So, I send in both.

Then, after half a week, the useless report status website switches to the status “on hold.” At that point I was rather certain, that this would not end well. Then, almost a week after I sent in the devices I get an e-mail from customer service, telling me that they cannot reproduce my problem. I am very surprised. I can only imagine that they never turned on the laptop, but that they were only search of scratches of the housing. More importantly, however, they ask me to provide a detailed error description! Otherwise, they would send back my device. I mean, honestly, I already provided detailed error descriptions and step-by-step descriptions to reproduce the error. Twice. But ok!

At the same day, I write an answer to that e-mail, with, again, a detailed error description and a step-by-step description of the minimal setup to reproduce the error, remember: the two devices and three cables, and a new, clean Windows 10 installation with only all updates installed. Easy enough. It is worth noting, that I wrote those three different error descriptions all anew. It was not one text copy-pasted three time. It was three texts, all always in the context of the questions and requests from customer service, all detailed, all clear, all polite. … Three times!

Because the customer service gave me a deadline of two days to send in this information, I contacted them directly via the web live chat on the day after I received the e-mail and I sent in the information. There a friendly customer service agent, or really well-designed bot, confirmed that my e-mail was received and forwarded to the service center agent. Ok.

Then, almost a second week later, my devices were sent back to me, Laptop and docking station. And, the big surprise was the repair report included in the package this time:

They explicitly stated that they did not “repair” anything, because they never got the required information from me.

Three times!

And, by the way, the ethernet port of my docking station is still broken. 100% of the time. 100% reproducible. Always. With a defective USB device being reported in the Windows Device Manager. 100% of the time. Always.

Three times!

Greek Letters
If you ever see someone using a capital xi in an equation, just observe them quietly to learn as much as you can before they return to their home planet.

I like this one. From my experience I can tell you, at least half of those are true.

To all the scientists out there, I challenge you:

Write a real paper, a good paper, which will get accepted at a decent conference or journal, but _replace all greek letters, by Wingdings characters_!

Hello everyone!

I am a bit late, but: Happy New Year!

And boy oh boy, this will be a year. I am so looking forward to what’s coming. Likely, the most important reason for me being happy at this moment is: I got a new job. Exactly with the new year, starting with January 1st:

I am now a Senior Software Developer at Microsoft’s Mixed Reality Team.

I am still in the middle of my on-boarding. This opportunity is great. I am grinning every morning when I switch on my workstation. And, until now, no day disappointed.

This will be a great year!

I got my “wisdom,” I like to call it that, from many different sources: family, friends, movies, … It’s also often called “useless knowledge.” I can live with that too. And things get most interesting, when we are talking about things which are not (fully) under our control. Opinions then become more and more reflections of how we see things, and not how they are. Well, opinions always are that way, but in those cases this becomes more and more apparent. Let’s take our jobs for example, we love ‘em, we hate ‘em, we need ‘em. If you’d ask me to look at it from an academic perspective, I’d like to first introduce a scale (based on my useless wisdom):

The upper end is defined for me by a quote from Confucius. Actually, the last time I heard it, was when my sister read it to me from Kermit the Frog’s Instagram.

“Choose a job you love, and you will never have to work a day in your life.”

And for the lower end, which is not that negative at all, I choose a quote from “The Devil Wears Prada.”

“Jobs that pay the rent.”

Well, that is not Confucius, but it certainly does hit the point we oftentimes feel.

Obviously, it’s an open scale from 1 – Love to 0 – Rent, and beyond at both ends. And, there certainly is something like “That’s it. Here is my resignation. Fuck you all.” as well, but I do hope that range is not needed very often. Let’s put that to -1.

So, there you are. A nice Scale between Love and Hate, about our jobs.

If you had one of those days, and you feel like close to zero, or even in the negative, then try to remember a previous day, where you were close to one. Got it? Keep at it. You’re goal for work is to make those days count! And to have more of those days that the others. And, for the other days, don’t forget about your life outside of your job as well!

One of the best Xkcd evergreens:

Fortunately, the charging one has been solved now that we’ve all standardized on mini-USB. Or is it micro-USB? Shit.

Yes, I am still using AntTweakBar. As you might know, the development of AntTweakBar is discontinued. At some point in the future, I will switch. Currently, I consider imgui the best successor. But I haven’t had time to look into imgui. So, when I resurrected an old small tool of mine, it still used ATB, and I did not want to recode all of this. But out of “because-I-can,” I decided  to update all dependencies to their newest versions. As a result the ATB integration with GLFW 3 did not work any longer. A couple of callback functions where changed between GLFW 2 and GLFW 3. I ended up rewriting my glue code between those two libraries.

Here it is, if any of you ever come across the same issue. First the callbacks:

static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
#ifdef HAS_ANTTWEAK_BAR
  if (action == GLFW_PRESS || action == GLFW_REPEAT)
  {
    int twMod = 0;
    bool ctrl;
    if (mods & GLFW_MOD_SHIFT) twMod |= TW_KMOD_SHIFT;
    if (ctrl = (mods & GLFW_MOD_CONTROL)) twMod |= TW_KMOD_CTRL;
    if (mods & GLFW_MOD_ALT) twMod |= TW_KMOD_ALT;

    int twKey = 0;
    switch (key)
    {
    case GLFW_KEY_BACKSPACE: twKey = TW_KEY_BACKSPACE; break;
    case GLFW_KEY_TAB: twKey = TW_KEY_TAB; break;
    //case GLFW_KEY_???: twKey = TW_KEY_CLEAR; break;
    case GLFW_KEY_ENTER: twKey = TW_KEY_RETURN; break;
    case GLFW_KEY_PAUSE: twKey = TW_KEY_PAUSE; break;
    case GLFW_KEY_ESCAPE: twKey = TW_KEY_ESCAPE; break;
    case GLFW_KEY_SPACE: twKey = TW_KEY_SPACE; break;
    case GLFW_KEY_DELETE: twKey = TW_KEY_DELETE; break;
    case GLFW_KEY_UP: twKey = TW_KEY_UP; break;
    case GLFW_KEY_DOWN: twKey = TW_KEY_DOWN; break;
    case GLFW_KEY_RIGHT: twKey = TW_KEY_RIGHT; break;
    case GLFW_KEY_LEFT: twKey = TW_KEY_LEFT; break;
    case GLFW_KEY_INSERT: twKey = TW_KEY_INSERT; break;
    case GLFW_KEY_HOME: twKey = TW_KEY_HOME; break;
    case GLFW_KEY_END: twKey = TW_KEY_END; break;
    case GLFW_KEY_PAGE_UP: twKey = TW_KEY_PAGE_UP; break;
    case GLFW_KEY_PAGE_DOWN: twKey = TW_KEY_PAGE_DOWN; break;
    case GLFW_KEY_F1: twKey = TW_KEY_F1; break;
    case GLFW_KEY_F2: twKey = TW_KEY_F2; break;
    case GLFW_KEY_F3: twKey = TW_KEY_F3; break;
    case GLFW_KEY_F4: twKey = TW_KEY_F4; break;
    case GLFW_KEY_F5: twKey = TW_KEY_F5; break;
    case GLFW_KEY_F6: twKey = TW_KEY_F6; break;
    case GLFW_KEY_F7: twKey = TW_KEY_F7; break;
    case GLFW_KEY_F8: twKey = TW_KEY_F8; break;
    case GLFW_KEY_F9: twKey = TW_KEY_F9; break;
    case GLFW_KEY_F10: twKey = TW_KEY_F10; break;
    case GLFW_KEY_F11: twKey = TW_KEY_F11; break;
    case GLFW_KEY_F12: twKey = TW_KEY_F12; break;
    case GLFW_KEY_F13: twKey = TW_KEY_F13; break;
    case GLFW_KEY_F14: twKey = TW_KEY_F14; break;
    case GLFW_KEY_F15: twKey = TW_KEY_F15; break;
    }
    if (twKey == 0 && ctrl && key < 128)
    {
      twKey = key;
    }
    if (twKey != 0)
    {
      if (::TwKeyPressed(twKey, twMod)) return;
    }
  }
#endif
}

static void charCallback(GLFWwindow* window, unsigned int key)
{
#ifdef HAS_ANTTWEAK_BAR
  if (::TwKeyPressed(key, 0)) return;
#endif
}

static void mousebuttonCallback(GLFWwindow* window, int button, int action, int mods)
{
#ifdef HAS_ANTTWEAK_BAR
  if (::TwEventMouseButtonGLFW(button, action)) return;
#endif
}

static void mousePosCallback(GLFWwindow* window, double xpos, double ypos)
{
#ifdef HAS_ANTTWEAK_BAR
  if (::TwEventMousePosGLFW((int)xpos, (int)ypos)) return;
#endif
}

static void mouseScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
{
#ifdef HAS_ANTTWEAK_BAR
  static double pos = 0;
  pos += yoffset;
  if (::TwEventMouseWheelGLFW((int)pos)) return;
#endif
}

static void resizeCallback(GLFWwindow* window, int width, int height)
{
#ifdef HAS_ANTTWEAK_BAR
  ::TwWindowSize(width, height);
#endif
}

Of course, you can omit the #ifdefs if you don’t care. Add your own codes to the functions after ATB has been handled.

Then, it’s just your typical initialization of GLFW callbacks:

::glfwSetKeyCallback(window, keyCallback);
::glfwSetCharCallback(window, charCallback);
::glfwSetMouseButtonCallback(window, mousebuttonCallback);
::glfwSetCursorPosCallback(window, mousePosCallback);
::glfwSetScrollCallback(window, mouseScrollCallback);
::glfwSetWindowSizeCallback(window, resizeCallback);