Note (2021-04-04): while Chrome and Edge appear to have fixed this problem a while ago, there are people who have reported experiencing this issue as of this past week. If you’re experiencing an issue described in this post, you should really quad-check that your browser is up to date.
As if Microsoft Edge screwing me over in 2020 wasn’t enough, as soon as Microsoft fixed their bugs, Google decided to introduce their own. In second-ish half of Januray, Chrome and Edge both received updates that broke pretty much every single extension for correcting video aspect ratios on ultrawide (and super-ultrawide) monitors.
This was accompanied with an uptick of one-star reviews on Chrome Web Store, which happened on most extensions like Ultrawidify — a sign that the problem is not being caused by extensions, but by Chrome.
Side note: while I am singling out Chrome in this post, this issue is present on ALL Chromium-based browsers, such as Edge or Opera.
Firefox is unaffected by this problem.
What’s going on
The bug only appears under certain conditions:
You’re using Windows
You’re using a nVidia GPU (may be specific to RTX 20xx and 30xx series)
You’re viewing a video in fullscreen mode
<video> element is both wider-ish and taller-ish than the monitor.
Item #4 gives us a possible workaround (other than “just use Firefox”) for the problem. Since the issue only appears if we zoom in too much, we can tell the extension to avoid zooming in past a certain point. And that’s exactly what I did.
If you’re using Chrome, the extension will always leave a thin (3-10px) wide black border at the left and right side of the monitor while in full screen. The fix for this problem came out around January 20th. It was designed to only activate in cases where the bug may appear.
Update (2021-04-04): as of Ultrawidify version 5.0.1, it is possible to manually enable or disable this workaround.
I am not affected by this issue. Can I disable this workaround?
Open the extension popup, select “Advanced settings” in the menu at the left side of the popup. At the bottom of advanced settings, there will be the Browser quirk mitigations section:
To disable the workaround, uncheck the limit zoom checkbox.
The video is still being incorrectly stretched for me
This is improbable: it appears that the issue is fixed for both Chrome and Edge. Ensure your browser, GPU drivers, and Windows are up to date. If you’re having this issue, chances are your system is out of date in some way. However, if you’re sure everything is up to date, there are further things that you can do.
First of all, check that ‘limit zoom’ option is checked (in: Extension popup > advanced settings > browser quirk mitigations; see the screenshot above). There’s a slight possibility that I decided to disable the workaround by default after publishing this post but before uploading new version of the addon to the Chrome Web Store.
If that didn’t work — it is possible that Chrome is unable to detect whether you’re using full screen mode or not. Try unchecking the ‘limit zoom only while in fullscreen’ checkbox.
Finally, if the issue still persists, you can also try decreasing the ‘Limit zoom to % of width’ number by a few percent.
If using Edge or Opera, please consider reporting the bug to browser developers
You can do so by pressing alt + shift + i. (Or look for ‘Report an issue’ option in browser’s menu. The option may be nested under ‘help’ option). This should open up a dialog like this:
In the ‘describe what’s happening’ box, enter something along the lines of the following:
If — while in fullscreen mode — CSS property transform: scale(x) causes video to be both taller and wider than the monitor, the video will be stretched to exactly fill the monitor. This is incorrect behaviour.
* If video and monitor are of different aspect ratios, aspect ratio of the video will not be preserved. (Expectation: transform: scale(x) should preserve aspect ratio if only one factor is provided) * transform: scale(x) will not scale video to a size that’s strictly bigger than the monitor. (Expectation: it should do so if x is big enough)
This issue only happens when the following are met: * Windows * Full screen and watching a video * Hardware acceleration: enabled * nVidia GPU. This issue appears primarily on RTX cards (20xx and 30xx). It likely does not happen on non-RTX cards (this issue does not seem to be present on a GTX 965m) * Console: closed or in detached window. If console is opened and docked to the side, the problem does not appear. * transform: scale(x) or transform: scale(x,y) is used to scale the video to a size bigger than the monitor
It’s been a nice autumn evening when my phone buzzes. One new email, says the notification. It’s github. Ultrawidify doesn’t work on Edge at all. Not on youtube, not on Netflix, nowhere. Curious, I think to myself and wow to not take the Valve strategy this time around. Which, of course, happens anyway because it turns out that my weekends are considerably less free than I thought they would be (Not to mention that my brain sometimes works the way Beinoff, Weiss and DeBlois say it should — something something kindaforgot). Recent reviews on Chrome Web Store seem to echo that issue (I’m at least 99.7% sure at least one, but likely more, of recent one-star reviews came from an Edge user, who has absolutely no business leaving reviews on CWS), though the majority is about issues that can be reproduced in Chrome.
So let’s see why Ultrawidify is broken on Edge, and why I pulled it from the Edge’s extension store, and why Edge users are getting this big ass red popup when they try to watch their Netflix.
Oh yea, spoiler alert: Ultrawidify is not broken on Edge. Edge is broken on Edge.
ELI3: Webdev 101
Long story extremely short: websites are just a bunch of rectangles. Sometimes said rectangles are inside each other, sometimes they’re next to each other, and it’s developer’s job to tell them where they should be and how big should they be.
And this is what Ultrawidify does, ultimately: it tells one of the boxes to get bigger.
The Player and the Video
Ultrawidify only cares about two of these rectangles: the player and the video. The video is a special kind of rectangle: you can control its size, but it’s kind of a black box. You can’t control anything inside it, and it’s the browser’s job to handle displaying things inside the box.
In normal, functional browsers, things look like this:
And when we encounter a video with black bars, things look like this:
As you can see, two things happen:
Red square gets taller than the blue square
Browser makes video fit the red square
But if you open youtube in Edge, you’ll find that youtube behaves in exactly the same way as it does in Firefox and Chrome. How come Netflix and the likes don’t?
Turns out: same reason why autodetection doesn’t work on Netflix (and Hulu and Disney and everything else).
Obviously, the studios try to make as much of their money back as possible. And that brings us to a problem. Once the movie is on the internet for free, it gets a bit harder to make money off it. After all, why would you pay for Netflix or Hulu or Disney+ when you can just get a browser extension that records (or even downloads!) the movie or show you want to watch, subscribe to the Netflix’ one month free trial, download everything you want and then cancel before you start getting billed.
This is why streaming sites attempt to ensure that recording and downloading the shows and movies they host is as difficult as possible. And while such DRM measures are not very effective — as new movie and show releases very quickly find their way to thepiratebay, YIFY, whatever’s the tracker of the hour right now — the DRM measures still raise the tech barrier significantly enough to be worth it.
DRM magic happens inside the <video> tag (or the red square), and it’s up to browser to handle it. Different browsers handle DRM differently, which is why Edge gets 4K netflix while Firefox and Chrome don’t.
Which Brings us to Microsoft Edge
Yeah, Edge’s DRM implementation is completely broken.
Okay, it’s not completely broken. If the video element (red square) fits completely within the player (blue square), things will display correctly:
However, the moment the video (red rectangle) becomes taller or wider than the inside of the browser window (in this case, also dashed blue square) … Well.
Remember: red square (top and bottom are outside the window boundaries in this example) is the video tag. We cannot control how things inside the red square appear, at all.
It appears that internally, Edge thinks that video (the red square) cannot be bigger than what’s displayed on the screen. This becomes super-evident when we push the video 25% off-screen:
Both the player and the video are pushed 25% towards the bottom. This means that the bottom 25% of the video should be cropped off — however, as you can see by the size of the top and letterbox, Edge simply resized the video so no cropping occurs.
The same thing happens if you push the video 25% over the top edge: edge will resize the video to 75% of the original size so no cropping occurs … except it’ll also align the stream with the top of the video tag / red box, meaning you’re now getting twice the cropping.
All in all, I see Microsoft’s acquisition of Bethesda is already paying dividends.
When you try to capture rectangular region of the screen, Spectacle (said screenshot utility) will freeze the entire desktop and allow you to draw a square
If you’re running a multi-monitor setup with high PPI monitor(s) under Xorg, the image of your frozen desktop will be moved partly off-screen.
This means that you will not be able to see or select a certain portion of the screen
My solution amounted to a few console commands bashed together that move the frozen desktop frame back where it’s supposed to be.
This has downsides. For example, the issue only gets fixed when you trigger screen capture with a keyboard shortcut. If you tell Spectacle to capture a screenshot after a timeout, the issue won’t get fixed. It’s also not guaranteed to work if you have multiple Spectacle windows already open from previous screen captures.
That’s not good enough.
What about fixing the problem at the source?
From this point forward, there are roughly two options. I can try to use Xlib (with which I had a very brief encounter some time ago) and watch for new Windows getting launched, or I can do the right way and fix Spectacle. After all, we are talking about open source software.
Thus I go ahead and clone Spectacle’s github repo. I start looking at the code and notice the first problem: it’s written in C++.
Still, I dig through the code, trying to find if there’s any place I can see that sets the spawn position of the frozen frame. One and a half hour later, I just give up. It’s time for Xlib.
X gon give it to ya
After giving up on Spectacle, I go back to whipping up my dirty hacks. I start shopping for functions that I want and incorporate them into my program. Eventually, I’m far enough to do a test compile. This is where I get to witness this bullshit.
We are witnessing a missing import. The question on the table is which. As we start looking for an answer, we run into another problem: C and Xorg were both designed before stackoverflow existed.
They were designed in a time where people assumed people will actually read the _entire_ documentation before trying to write their first “hello world” thing. The “you need to import this file” will be hidden as an offhand mention in one of the two tutorials you were linked to kick things off (and it’s always gonna only be in the tutorial you didn’t check), and then never again.
If you’re doing your function-shopping on stackoverflow code or by browsing the list of functions on websites that are basically manpages but with links, you will miss that. Documentation and manpages for functions will treat the necessary import as if it were a recipe for coca-cola.
It’s X11/Xutil.h. Is it really that hard to put on a manpage, or am I just missing something because I have literally no experience?
Anyway, back to track.
The program is set to log window titles, which it does … Except the values aren’t quite what we’d want. We expect to see “Spectacle” appear (and nothing else) — but nothing of the sort pops up:
What gives? There’s two thing, actually. One: we’re requesting “WM_NAME” property, when we really want “_NET_WM_NAME.” Second: we’re requesting CreateNotify events, but Qt may be changing titles (and geometry) after creating windows. This means we need to catch ConfigureNotify events as well.
When we listen for ConfigureNotify events as well, we finally get what we want:
The hard part seems to be over now. All we need is another strcmp to compare _NET_WM_TITLE with and XMoveWindow, both pretty straightforward. (And we can’t forget to set up an error handler, because BadWindow will happen every now and then. We can ignore this error).
Now let’s try to figure if this hack can work with vlc and smplayer as well …
Where we look into what makes an ending meaningful and satisfying, and what doesn’t.
How To Train Your Dragon: The Hidden World and Dragonheart. If you squint a bit (or a bit harder), the movies seem to sport some similarities in terms of the plot. Both movies feature protagonist who befriended a dragon. They end in very roughly similar way, too — with a sacrifice and the protagonist and his dragons separated forever(-ish — Hiccup gets to see Toothless once more, but the separation is a bit more permanent in the case of Dragonheart).
Despite both movies share some mild similarities and end in a bitter-sweet way, one of the movies is among my favourites — and the other is … well, on the other side of the spectrum.
Of Meaningful Endings and Satisfaction
Some people will tell you that bittersweet endings are often more meaningful than happy ones. They’re certainly a bit rarer than happy ones, for sure — but more meaningful? Well, it’s only meaningful if it’s justified. If the ending is arbitrary or feels arbitrary, that undermines any meaningfulness of the movie.
In order for the ending of that movie to be justified, one needs to start with one simple question: what reason does the movie give for it’s ending? In case of Dragonheart, the question is what reason does the movie give for Draco’s death; with The Hidden World, it’s what reason does the movie give for departure of dragons.
If the movie was any good, you could point at some scenes and say that they have to leave because in this scene, that happens. Once the proverbial bucket is full of answers, it’s time to ask some follow-up questions: could a movie support a different ending without any changes?Does anything (be it actions of the protagonist, an event, or general worldbuilding of the movie) contradict the reason the movie gives? Protagonist could, in theory, always take make a different decision in the end — but if the movie is good, it will not only justify the decision the protagonist took, but it also needs to show that decision the protagonist made is absolutely the best possible one. It needs to show that other options are either bad of non-existent.
This is what makes endings feel fulfilling and satisfying and deserved.
Quick side note: exceptions exist. There’s entire movies, books, and games that intend to show that at the end of the day, your decisions don’t matter. Dragonheart and How To Train Your Dragon largely don’t fit this bill.
Meaningful endings are similar, but they often contain something extra. In case of Dragonheart and The Hidden World, this something is protagonist making a sacrifice for the good of others. However, in order for the sacrifice to have any meaning, the movie has to — as I’ve said before — show that “good of others” absolutely and undoubtedly cannot be achieved without said sacrifice.
Consider, for a moment, Dragonheart
Dragonheart has its flaws — especially in the dialogue department. But despite its less than inspiring score on IMDB (probably stemming from the fact that before the success of Lord of the Rings, fantasy movies were often seen as inferior just because they were fantasy), the plot of the movie is rock solid and the writing is sharp.
Most importantly: Dragonheart justifies its ending very well.
The movie starts by future king Einon training with Bowen, a knight who highly values honor and other knightly virtues. The movie establishes very early on that Bowen hopes to teach Einon the virtues, but is as quick to suggest that Einon isn’t very receptive to his teachings. After training is over, Einon ends up rushing straight into the middle of a peasant revolt and gets himself impaled on a sharp stick. He’s rushed to a dragon — proper dragon, not a wyvern, voiced by none other than Sean ‘this is how dragons would sound if they could speak’ Connery. The dragon takes some pity on the boy and donates him half his heart, saving his life.
Bowen — thankful that Einon’s life is saved — wows to do whatever the dragon asks of him, whenever there’s a need.
As the — now current — king Einon gets better, he starts treating the workers in his quarry (prisoners from the peasant revolt at the beginning) in ways cruel enough to make working in an Amazon fulfillment center feel like a holiday in a five star hotel. Furthermore, Einon takes plenty of joy in being cruel and seeing peasants suffer.
Bowen doesn’t. Furthermore, he even blames the dragon for Einon’s behaviour, thinking the lizard bewitched the king. After all, how could a boy with a teacher like him grow up to be this devoid of all virtues? He returns to the dragon’s lair and gives a new wow: to hunt him down, no matter what it takes.
In the next dozen or so years, Bowen is in full Grimmel mode, killing dragons left and right. Eventually, he founds a dragon who offers him an alternative, to which Bowen eventually agrees. By day, the movie shows the duo conning Einon’s lords out of money with a pretend-dragonslaying scam; by night, the movie keeps reminding us that the heart bounds the dragon with the king: every time Einon gets hurt, it’s the dragon who feels the pain.
Eventually though, their con runs to an end. Along the way, the duo picks up a woman by the name of Kara, who — to keep a long story short — convinces Draco (and later also Bowen) to help her start a rebellion against king Einon. Rebels march at king’s castle. King notices the marching peasants, gets angry and rides out of the castle to attack them.
During the attack, the king gets shot through the heart. King survives, taking no damage from the attack; while the dragon is seen falling into the castle courtyard, where he is bound by king’s men. Bowen — who now considers Draco his friend — and Kara make a rescue plan. In the evening, they break into the castle, but are quickly discovered by king Einon.
Fight happens and eventually ends with Einon falling from the castle roof into the cellar. With Einon out of the picture, Bowen gets to Draco and discovers that he’s got roughly two options:
Kill the dragon or kill the dragon.
By this point, the movie has made it pretty clear that Einon cannot be killed. Draco even tells us that only his half of the heart holds the life force, that he must be killed in order to get rid of Einon, which disqualifies that option.
Keeping Einon alive and rotting in the dungeon seems inviting idea at first — until you remember that Draco feels Einon’s pain (the movie shows this at various points). In addition to that, the movie shows us that the fall broke Draco’s wing:
Which suggests that even if Einon’s suffering wouldn’t cause pain to the dragon, it’s very likely that Draco would never been able to fly again. Remember Gobber’s mantra?
This only really leaves Draco’s death the only possible ending for that movie. It also fulfills both oaths Bowen made to Draco: his oath to kill him for corrupting Einon, as well as his oath to serve him for saving Einon.
For everything that happens in the end, there exists a trail of earlier movie scenes that justify the events that happened. It makes up rules of the game and then never ever breaks or contradict them. This is what makes for a good plot. This is what makes for good writing.
The Hidden World: (Not) Discovering Something That Doesn’t Exist
If you paid attention during the previous section, you might have noticed that Dragonheart keeps doing things that keep justifying everything that happens later down the line. If you ask yourself: “why does this happen in the end of the movie,” you can point at multiple scenes justifying that and none that dispute it. Meanwhile, The Hidden World might just as well be giving a monkey a shower.
You know what? You’re probably here from reddit, so you probably know my opinion on The Hidden World. You’ve probably seen it over half a dozen times by now. I probably don’t need to include the rest of this post, but just for the sake of completeness — here’s how The Hidden World compares against that. TL;DR: it disintegrates faster than Challenger on its last ride up.
Earlier, we established that in order to justify the ending, we have to do two things. We need to ask ourselves: what reason does the movie give for departure of dragons? Finding a few things that could sorta work as an answer is reasonably easy. Unfortunately — unlike with Dragonheart — the answer to the follow-up questions (could a movie support a different ending with no changes? and does anything contradict the reason the movie gives for departure of dragons?) is most certainly not a resounding ‘no.’
Dragons have to leave because one day, trappers might become a real threat
That’s a very hypothetical answer that’s not very satisfying. Worse yet, the movie disagrees big time. In battles with Hiccup, dragon trappers are shown to be incompetent beyond help and seem to pose no real danger. Their defeat comes easy.
Berk is not safe from trappers
Let’s just say that the worldbuilding somewhat disagrees with that one.
The Hidden World is safer from trappers
If you take what movies tell you at face value, yes. It doesn’t check with the worldbuilding that the movie does, though. If you consider that trappers know of New Berk, can whip up infinite number of ships, are adept at their job when Hiccup is not around and that we know that lifts exist, the answer changes a bit.
The answer changes completely once you consider that the waterfalls are shallow water and would thus allow you to land there (by running ship aground) — and let’s not forget about all the rocks sticking above the water.
Toothless is now king of all dragons in The Hidden World, he has to be there
Why does he have to be? Neither this nor the previous movies give a compelling reason. The “chief protects his own” theme works well enough even if it’s only limited to dragons and wyverns of Berk. Furthermore, what did he do to earn his place as the leader of The Hidden World?
Thotfury fears men (or some other thotfury bullshit)
Let’s forget that light fury grows less and less hostile towards Hiccup and Berkians throughout the movie. That alone would be enough to negate the separation, but as I said, let’s forget about it. Let’s pretend this is a good enough reason for Toothless to leave (which it isn’t). Why do all the other draconids have to leave with him?
Dragons do what alpha tells them to do
So why doesn’t Berk move with dragons and wyverns to The Hidden World?
Dragons attacked Hiccup and Astrid when they found them in the hidden world
So, in other words, dragons don’t do what alpha tells them to do. Why does every dragon follow Toothless and thotfury?
Dragons do what alpha tells them to do
The search is a mystery, ain’t it? Deja vu is hitting so hard you can basically hear the song now. But we won’t be doing infinite loops.
Humans physically can’t live in The Hidden World
There’s not a single scene or anything in the movie that would serve as evidence of that.
Another question that pops up regarding the separation — why can’t dragons visit?
Dragon trappers could follow dragons to The Hidden World
Wonderful how the “but it’s impossible to find and raid the hidden world” excuse goes out of the window the moment you ask that question. Besides — what is night?
Discrepancies like this make for a bad writing
The truckload of contradictions and no solid justifications we see in The Hidden World makes the ending feel arbitrary — and that makes it not very satisfying and even less meaningful.
I am sorry, but this is not good writing. How To Train Your Dragon franchise was built on something much better than that.
Spectacle is KDE‘s screenshot utility. Since the beginning of time (but only reported years ago), it has a bug that annoys people using multiple high or mixed PPI monitors. For example, consider my desktop. It looks like this:
5120×2160 at 34″ gives us a PPI (pixels per inch) of about 160. This has its benefits, but also some drawbacks — especially on Linux. Wayland solves some of the issues in that regard as it allows you to set scaling factor on per-display basis. Unfortunately, there are show-stoppers that prevent me from going that way.
This leaves us with Xorg. Xorg doesn’t do mixed PPI setups at all, but there’s workarounds. You can tell Xorg to pretend your low PPI monitors run at a higher resolution than they actually are. This has downsides (blurred image), but it beats the alternative. So you set your scaling factor to your highest PPI monitor and “supersample” other monitors appropriately.
The ‘scaling factor’ is set by your desktop environment — in my case, KDE (which uses Qt). If you set this scaling factor above 1, you will sometimes into a bug (in Qt). It pops up in a few different places, most annoying of which is when you want to take a screenshot of a rectangular region.
The unwelcome offset and duplication of the screenshot area renders some parts of the screen impossible to screenshot using rectangular selection. This is annoying. Let’s fix it.
KDE’s window manager, kwin, is pretty nice thing. Among other things, it allows us to tell windows and programs how to behave. We can force windows to stay always on top, we can force windows to open on a specific monitor, we can prevent windows from being closed. We can make windows transparent and tons of other things, but most importantly: we can make windows appear at exact location we want them.
The still frame of our screen on which we select is technically a full screen window covering all three screens. We only need to tell kwin how to recognize that window and move it to where we want. In order to that, we need to know two things: window class and window title.
We don’t know them yet, but the ‘Detect Window Properties’ button looks inviting — except it doesn’t work when we try to detect properties of our “draw rectangle for screenshot here” window.
Okay then, we’ll run xprop in the terminal while the window for drawing rectangular selection is active and click it.
Fine, clicking the window is not the only way to coax data we want out of xprop. We can give it the window id, like so:
This works. We set this command to run while rectangular selection for spectacle is active, we get what we want:
Window class: spectacle. Window title: Spectacle (as opposed to filename — Spectacle of the regular window). This is enough for us to only move the rectangular selection window, while keeping the spectacle window that appears after we’ve taken a screenshot where it is.
Kwin rule is thus written. Another screenshot is taken. The issue persists.
How to waste an hour.
Where there’s a whip, there’s a way
But we’re not giving up quite yet. The good thing about Linux is that you can do a lot of things via terminal — such as, move windows around. xdotool can do that. The command looks like this:
xdotool windowmove <window id> <x> <y>
And we already know how to get window id. We put what we know together and paste our command to the terminal:
During the five seconds of grace, we try to screenshot a rectangular selection of the screen. We get the first real success of the evening — after the five seconds are up, the rectangular selection window moves to where it should be.
Now, let’s try to launch spectacle’s rectangular selection and the code that corrects its position at the same time:
… and now, it suddenly doesn’t work anymore. Spectacle launches, but the rectangular selection window remains offset. Of course, we can swap positions of spectacle and commands that correct the offset of rectangular selection window, like this:
Wouldn’t be nice if this would happen when I pressed ‘print screen’?
Yes, actually, yes it would. So we open the custom shortcuts settings, provide a key shortcut, paste our command into the command box.
We click okay. We try to take a screenshot. Nothing happens.
But let’s not give up. Files with bash scripts also counts as a command. We thus take this command and paste it into a file (and take care to not forget to put the #!/bin/bash in the topmost line), save, make it executable and jot down the path to the file in our custom keyboard shortcut.
And it finally works — after about two hours of experimenting.
I’ve done the long ass rant in my previous post so I can focus on my road to solution in this one. Long story short:
I got a touchpad
I have multiple monitors, which means I have to drag my finger across the touchpad a lot
Cursor gliding is a solution
(Linux) drivers don’t do cursor gliding. They do coasting, but they’re shit at it.
I want to have that feature, but nobody’s going to write it for me.
We have a challenge, then.
What is cursor gliding?
Coasting a feature that was around as far as 10 years ago, but seems to have disappeared. The gist of it is: if you move the cursor fast enough with your touchpad, the cursor will continue moving even after you have lifted your finger. Since picure is worth a thousand words (and video a thousand more), here’s a quick demonstration:
Let’s get it on
I am, of course, not very familiar with C. I know some basics, but that’s about it. Writing proper drivers is beyond my skillset, and it sounds complicated. I don’t like complicated, nor do I want to spend time learning stuff for just one projects. I want a quick solution, so “proper driver” way route is … it can’t go.
I have had some previous experience with xev and xinput, though (back from when I tried to hack together support for certain keyboard keys that weren’t supported under Linux). As it turns out, xinput has a debug mode that will print all events emitted by a given input device — and we’ll be using that.
I have tried to find a proper-er way to intercept movement events, but I ultimately haven’t found any. There was one C library that appears to be broken for the past few years, and then there’s Xlib. I’m sure that if I spent some time reading the documentation, I could implement this proper with it. I just don’t want to lose a week doing so.
Piping xinput output to our program it is, then.
We still need Xlib to actually move the pointer (and get pointer position, since xinput tells us only how far the cursor has moved, but not its position). Scouting ahead, the pointer can be moved with XWarpPointer(). That function requires absolute coordinates of where the mouse cursor is to be moved, and this will be important later.
Parsing the data
As it turns out, piping output of one program to the output of another is fairly easy in C (although finding some examples of how to do that turned out to be a bit harder). You fork the program and in one of the forks. In the first fork, it takes about five lines of code to set everything up:
In the main thread, receiving said stream in something that can be parsed with getline() isn’t hard, either:
Things get a little bit more complicated from this point forward. We have to write a parser that will parse whatever xinput gives us. This is less easy, because goodies that other languages give us — such as trim() and startsWith() — do not exist in C. Fortunately for us, stackoverflow exists and provides us with copy-pasteable solutions which are ready for the taking.
With that in mind, we can continue with the main loop that will parse our events. Before we do anything at all, though, we need to look at the output we’re going to parse:
The first line tells which event xinput gave us, so we ensure it matches EVENT type 17 (RawMotion). Then we ignore the next three lines. The data we want is below the valuators: line, which we have to parse. It’s a fairly straightforward affair. Once the valuators section is over, we start moving the cursor.
Don’t t(h)read on me
Now that we have a place where we can start moving the cursor, we want to start think about moving it. We don’t actually want to start cursor on every movement, just when we lift the finger — which means that we need to define what “lifting a finger” means for us.
Turns out that the only way to detect that finger was lifted is to wait a bit and see if any other movement events happen while we wait. This is a problem: if we just throw a usleep(<50 ms>) into our code, then our program will first wait 50 milliseconds, decide that it’s been more than 50 milliseconds since the last event, move the cursor and only then process the next event. Solution? Threads.
With parsing and threads sorted out, we can finally start moving the cursor.
I like to move it
This is not a rocket science, either, but it did come with some trial and error attached. You start by calculating the speed at which the pointer moves. There’s two ways to do handle speed:
track speed and angle for the pointer
track speed of pointer along each axis
We don’t need to know absolute speed of the pointer, neither do we need to know the angle. More importantly, moving the mouse requires us to know x/y coordinates of where we want to move our mouse, which makes the second option the more sensible one.
We can’t just determine the speed from the final amount of movement we received from xinput. Most notably, since we don’t know when the finger was lifted, we cannot rely on the last measurement of movement to be accurate. For this reason, we keep a record of the last few measurements and — when end of finger movement was detected — average them. This will also help with the fact that touchpad may produce noisy data: calculating average speed from last few samples will help us to ensure that the speed at which the pointer continues to glide across the screen about as fast as we’d expect.
Moving the pointer is easy enough — every 1/60th of a second, we update the cursor position. This is simple enough: we store the last position (preferably in a float or double) and increment it by the movement speed. We also don’t want the mouse pointer to scroll forever, so we decrease it by some amount after every update. After the cursor speed has been reduced to near-zero, we can also stop moving the mouse and exit the thread since the speed is now too insignificant to actually matter.
Tada we’re done. Time to push it to github and call it a day. Thxbai we’re done here.
But then thing stops working 20 minutes down the line.
And memory leaks aren’t all — as I tweak around with touchpad sensitivity settings, it quickly realize that, at the sensitivity I find ideal, the diagonal movement of finger across the touchpad results in a very pronounced staircase-shaped movement of the cursor.
I guess it’s time to stop coding and start doing warranty returns.
Quick note: I initially tried to keep the intro short, but quick history of touchpads ended being not … so quick. This post thus mostly talks about quick history of touchpads, available options and why you might want to get one.
Despite being in just about every laptop, touchpads seem to be a very neglected piece of hardware that nobody seems to use if they don’t absolutely have to. And I sorta get it: even as recently-ish as 10 years ago, touchpads were outright terrible. They often didn’t support gestures beyond two finger scroll (or dedicated scroll area at the edge) — and forget about double tapping to right click, or triple-tapping to middle click. They also tended to be small. This meant that if you had to move your mouse cursor from one end of the screen to the other … well, you’ve got roughly two choices:
Move the cursor 10% of the way across the screen, lift the finger and move it back to the other side of the touchpad where you started, move the cursor a little bit again, repeat
Set mouse sensitivity and/or mouse acceleration so impractically high you wouldn’t be able to hit those buttons.
Synaptics — one of the more famous names in the touchpad business back then — was aware that both options sucked, big time. Thus, a handy feature was born: coasting. If you moved your finger across on the touchpad, the cursor would continue to move even after you lifted the finger — until you stopped the slide by tapping or moving your finger.
Fortunately, the multi-touch innovation in the world of mobile phones (started with the first iPhone in 2007) eventually ended up trickling down to touchpads as well. We started getting bigger touchpads with multi-touch support — big enough that cursor coasting was no longer necessary for most people.
Depending on what make of laptop and operating system you have, touchpads are still cancer to use. Asus, for example, did not include a way to disable “natural scrolling1which is a retarded feature that should have never existed — but just like every step back Apple “innovated,” others were soon to copy. Removal of 3.5mm audio jacks is possibly the worst trend to date. ” You could, of course, download an updated touchpad driver which provided more options — but the next Windows update will just revert to the previous, useless driver.
This — as well as the fact that most people are used to using a mouse — probably contributes a lot to why touchpads are rarely seen on desktop. A year and a half ago, there were only two-ish viable touchpad models for the desktop that you could throw your money at. One was Apple’s trackpad for waaay too much. The second, more reasonably priced option, was some JellyComb model for about €50 from German amazon.
Back at the time, I was struggling with wrist pain while using the mouse for about two weeks. I quickly learned that touchpads are great for avoiding wrist strain. I promptly ordered two, as I really don’t want to develop a carpal tunnel.
As you look at the picture above, you might notice a few potential problems with that touchpad models. Most notably, there’s some unneded buttons that you could press by accident. You could disable those, but the button that toggled extra buttons on or off was itself very easy to press by accident. USB-micro connector on the touchpad was also pretty fragile, so both touchpads got broken in less than two years. I attempted to re-soldier, but failed miserably. I had to get new touchpads.
The new models are better: the entire touchpad is a button that you can click and it doesn’t have any soft buttons. But as I started to re-adapt myself to the touchpad life, I really started to miss cursor coasting (and scroll coasting). My main monitor is 34″ (ultrawide), with an extra monitor on either side … Needless to say, there’s no touchpad big enough for that.
State of drivers on Linux
The first thing that I tried to do was to turn on coasting in touchpad settings. But I was in for a rude surprise: there is no coasting option in the driver.
Why isn’t there a coasting option in the driver?
Because Synaptics driver has been deprecated for years and replaced by libinput, and libinput is hot trash. Of multi-touch gestures, it only supports two-finger scrolling, two-finger tap and three-finger tap. If your touchpad supports click, your choices for right-click strategy are “two finger click” or “click in the lower right corner” — pick one. (If I had the chance, I’d enable both of these at once. In some situations, two-finger click is better than clicking in the lower right corner. In others clicking in lower-right corner is preferable — e.g. if you try to drag something with right click).
There is a third alternative: mtrack. Mtrack is great. It mitigates a lot of xinput shittines, but it comes with its own set of downsides. The original repository appears to be unmaintained for the past five years. There’s plenty of unsolved issues and pull requests that await merge or any sort of decision. If you want the most features out of it, it’s best bet is to find an alternative fork. Of those, p2rkw’s branch seems to be the most promising one, with most features and best documentations.
Then there’s configuration: you pretty much have to configure the driver with xinput and a xorg.conf.d file, which is … less than ideal.
But for the price of being a major pain in the ass to setup, you do get some benefits. If your touchpad can be clicked like a button, then you can set mtrack to ignore finger movement when you’re clicking the touchpad on the bottom end. It allows you to both reserve bottom right corner for right click, while still maintaining ‘click with two fingers elsewhere to right click’ functionality. It even allows for scroll coasting (but not cursor gliding), but … it kinda sucks. Scrolling appears to go haywire if you start scrolling before the scroll coast has ended. Duration of the scroll coast is also fixed: it always coasts for 5 seconds, regardless of how fast you scrolled.
I obviously don’t like that. Unfortunately, nobody seems to want to do anything about it. The common mantra in open source world is that “if you don’t like it, do something about it” — and given you’re getting software for free, that seems a reasonably fair deal. The problem is, of course, that not everyone can write software. Even among programmers, not everyone has the experience to write everything. I know jack shit about drivers and the last time I used C (which is what the drivers use) was 4 years ago at the uni.
This means that I don’t really have the qualifications for writing a driver, so I can forget about doing things with mtrack. But with a very limited knowledge of C, I might be able to cobble something up in a different way …
It’s been a while since I’ve written about programming, but lately I’ve gotten back to working on Ultrawidify. With no major bugs or problems that require immediate fix, I can finally get to work on bugs and features that I’ve been kicking down the line for a while.
Problem of the week are, of course, keyboards (or keyboard layouts).
To elaborate a bit further on the problem: a significant chunk of Europe uses QWERTZ layout. QWERTZ is much like QWERTY layout Americans (and the rest of Europe except France) are using, except ‘Z’ and ‘Y’ trade places. French weren’t content with swapping only two letters and came up with with AZERTY layout instead. Then you also have things like Dvorak and Colemak layouts, because some people insist it makes their typing much faster. As letters trade places around the keyboard with each different layout, the keycodes don’t.
As a result, you can never be sure what letter the end user get from pressing what key. On some layouts, keycode 90 will give you Y, on others Z. This means that if you base your keyboard shortcuts on a QWERTZ layout and have ‘Z’ as a shortcut for anything, French and QWERTY users will wonder why they have to press the Y key, and Dvorak users will just shout at you that keyboard shortcuts don’t work.
If your goal is to have keyboard shortcuts that won’t be flat out wrong for people using a different keyboard layout than your own, then keyCode is not the way. Handling different keyboard layouts is an easy road to code spaghettification. Most importantly, it’s a major pain in the ass. You’ll spend a lot of time on it, but with very little gain.
Neat. Now I can just use this property for all my keyboard shortcuts. Since event.key gives us a specific character, I no longer have to pay attention. I can just say “press Z to zoom.” After all, the ‘z’ that event.key gives me is exactly the same, regardless of whether the user uses QWERTZ, QWERTY or something more exotic. Foolproof, isn’t it?
Wrong, sir, wrong.
True, event.key will return the same letter regardless of what keyboard layout you pressed said letter on. However, some letters are unpressable on certain keyboard layouts. If you’re using cyrillic (or anything non-latin), you’ll quickly find that keyboard shortcuts using even the standard ASCII letters no longer work.
Certainly a mild oversight on my part, but in my defence: the only reason I’ve started developing this extension is because at the time, there was no extension for fixing aspect ratio available for Firefox (Chrome did have a fair share of aspect ratio fixers such as Ultrawide Video, but those hadn’t been ported to Firefox until way later) and I really wanted the functionality. And when all you want is a swingset, why build a rollercoaster? The good old days.
But let’s get back to the topic at hand. If we want to fix the Russian problem, we’re in a bit of a tough spot. event.keyCode is starting to look better and better by the minute … except it doesn’t, really.
What can be done?
The options roughly boil down to the following:
1. Use event.keyCode to determine keys.
This option brings a lot of problems. Not only will there be issues with people using non-QWERTZ layouts (unless I spend unreasonable amount of time working on getting around that), using event.keycode would mean I have to rewrite lots of the existing code. More importantly — since all keys have been fully rebindable for a while in extension settings, I would have to decide between writing something that will correctly preserve keyboard shortcuts for existing users (annoying, quick StackOverflow recon didn’t give encouraging results), or reset keyboard shortcuts to default for everyone (easy but rather unacceptable. I don’t want another Nosedive).
2. Use event.keyCode to determine keys for new users, event.key for existing ones
This one offers some benefits over purely keycode solution — I don’t have to write code to port keyboard shortcuts to the new system, I don’t have to wipe settings of existing users. Still has some drawbacks that I don’t like, though — namely, the fact that I’ll have to deal with displayed keyboard shortcuts being wrong for non-QWERTZ keyboard layouts.
3. Keep using event.key and fall back to event.keycode if event.key doesn’t contain an ASCII character
Hey look, this is the quick and lazy solution we’ve been looking for. It’s also dirty, but it’s going to work. Maybe not on custom shortcuts, but we’ll see.
Nine years after How To Train Your Dragon was released, people still like to argue about just how intelligent Toothless (or other draconids of the franchise) really are. Some people argue that they’re intelligent and sapient, other argue against with mostly dumb excuses (the most retarded ones to date are vague fog of “but they aren’t human” and “they don’t believe in gods”).
One of the big ticket items in discussions about how intelligent draconids are — especially Toothless — is the ‘Astrid goes for a spin’ scene, in which Toothless ignores Hiccup and proceeds to give Astrid the Guantanamo Bay package. Right after that, there’s also the incident where Toothless takes Hiccup and Astrid to see Green Death1And yes, Dreamworks. Green. That last minute script change is a load of bullshit. Expense some Enchroma glasses on Dreamworks’ credit card.. The rationale for Toothless’ intelligence in that scene goes like this: When Astrid mentions that Hiccup will “have to kill a dragon,” Toothless gets a very bright idea. He understands what Astrid is saying. He understands the repercussions of what Astrid is saying. So he raises his ears, listens for Green Death’s noises and decides to submit his wishlist item. Green Death must die.
But of course, that point gets contested really quick.
The two main counters to this theory are “no, Toothless was under the influence — or flat out mind controlled — by Green Death, he doesn’t do that out of his own free will” and “dragons aren’t humans, therefore they aren’t smart enough to do that.” Some may also object with “Toothless got alert before Astrid mentioned killing a dragon,” but we’ll come to that later. Let’s debunk the mind control issue first.
Green Death and Mind Control
Some people really like to point out that Green Death has some sort of control over the draconids, that Green Death can magically make dragons and wyverns return to the nest. Why else would anyone return to a home like that if they weren’t charmed?
The oft-cited evidence in favour of some sort of mind control is that Toothless’ eyes narrow when he starts hearing the signal, but that’s really not what happens.
First of all: mind control is invention of the second movie. I’m somewhat reluctant about explaining things in the first movie with (or accepting explanations from) the subsequent movies. This is mostly because the second and third movie haven’t even made it to the idea stage when the first one was being produced. There was no plans — let alone specific ones — about what will transpire in sequels, which means that forward compatibility is not guaranteed at all. This means that arguments based on ‘this happened in the sequel, therefore it must hold true for the prequel’ kind of thinking are sorta invalid.
There is one trick that you can use to get around that, though. The sequels generally don’t contradict the original movies (although How To Train Your Dragon: The Hidden World, contradicting the first two movies as well as itself, is a massive exception in that regard). This has one neat effect that we can exploit. Since the no-contradiction rule implies that if something exists in both the sequel and the original, it will work exactly the same way in both movies. With the case of How To Train Your Dragon, theorized mind control does not behave the same way as confirmed mind control in the sequel, therefore we can be sure it didn’t happen.
And we can see that rather clearly:
Note that if the result was the opposite — if mind control in the second movie exhibited similar properties as something in the first one — that still wouldn’t be a sure confirmation. As it turns out, it’s entirely possible that two different things share the appearance.
There’s further evidence against outright mind control that you can find using this so-called “rule of consistency.” In HTTYD2, Toothless pretty much can’t do anything while being mind-controlled. In HTTYD1, however, Toothless willfully and deliberately ignores Hiccup’s plea to turn back:
But if that’s still not enough to convince you, consider this: during the final confrontation, it takes a grand total of three rocks2Actually, the real number is probably around a dozen or so, but by not exaggerating I would lose some of the entertainment factor and one glowball to make every dragon and wyvern fly away instead of fight. This right here is the clearest evidence against the mind control.
The Nature of Green Death’s Signal
“So what is Green Death’s signal if not mind control, then?” one might ask, “and why do all dragons and wyverns follow it?”
No, it’s not any sort of hypnosis or charm or brainwash either. If the presence of the signal would be forcing Toothless to follow it, he wouldn’t have had the capacity to show any sort of resistance to Hiccup. Toothless wouldn’t reject Hiccup’s plea, he’d ignore it without a reaction.
Green Death’s signal is a beacon.
Nothing more, nothing less. It’s a signal that shows dragons the way home and not much more. Green Death obviously doesn’t treat her draconids too well, but if human victims of domestic violence can rationalize returning home to their abusers for unreasonably long amount of times, so can draconids.
Furthermore, it is entirely possible that dragons find safety in the nest. Sure, they might get eaten by the Green Death — that’s surely a con — but the nest provides many benefits. It’s a damn good shelter, for one, and contrary to what How To Train Your Dragon: The Hidden World and Ensiferum would have you believe, there is, in fact, strength in numbers. Sticking together makes dragons and wyverns safer from human raids — doubly so when you’ve got a dragon the size of a mountain defend the nest against the invaders, be it vikings or other dragon nests looking for new territory.
At this point it’s worth noting that the first movie never explicitly confirms or denies existence of other nests. However, much like there’s not just one anthill, it’s reasonable to expect that there are other nests like the one near Berk that exist in the world.
How To Train Your Dragon 2 seems to mildly agree with this notion by introducing us to Valka’s sanctuary and Drago’s army, both of which can be classified as a nest of sorts; but we said we’re not paying much worth on “evidence” from the second movie.
Meanwhile, if the draconids opted to live outside their nest, they would be vulnerable against vikings and against dragons that weren’t part of their group. Given that Vikings have supposedly killed ‘thousands of them,’ I reckon staying with the nest is a good idea.
Obviously, the movie ever so slightly contradict this point by draconids wussing out as soon as Vikings break out the superior siege engines, but that’s not the problem with this theory. That’s a minor plot issue/self-contradiction on the part of the first movie: namely, we know that dragons and wyverns will defend the island against vikings in the rocky labyrinth surrounding it. Why not once the vikings land?
Congratulations! You found pretty much the only problem the original movie has.
Actually, it’s not the only one, there’s a second problem that relates to Vikings just blasting the mountain with three rocks. But the point is this: you can trust that proponents of The Hidden World are just gonna take those two problems and throw them around with the zealotry of an anti-vaxx mom who found this one google hit agreeing that vaccines cause autism. Likewise, you can trust that they’ll start to engage in some blatant both-sides-ism, pretending that having two minor problems is the same as having two hundred (with twenty of those pretty major).
The problems with dragons wussing out can be partially handwaved away as “they’re just moving out of the way of the Green Death and allowing it to do it’s job” — but hey, we’ve derailed this post for long enough now.
The point is this: Green Death signal is a beacon, not mind control. Dragons and wyverns live at the nests because they want to. They live there because the nest is a net benefit, not because Green Death would want them to. Believe me: if you had to pick between a shit, run-down apartment and living under a bridge… Let’s just say that you wouldn’t pick a bridge, either.
I Looked Him in the Eyes and Saw …
Yes, I know. The ‘mind control’ counter-theory is pretty much dead by now. But we’ve got further ‘evidence’ that Toothless’ eyes and face don’t indicate mind control. Toothless has had such eyes at various points in the movie. Let’s see some screenshots.
So yeah: it turns out that Toothless’ eyes turn narrow every time he’s scared, guarded or angry.
Long story short: mind control doesn’t seem likely.
“But Toothless isn’t Smart Enough”
I’ve seen some people try to argue that Toothless is closer to being a Jerry than he is to being a Rick (or even at least a Morty) … But that doesn’t hold up all that well, either. There are at least two scenes that require near- or even human-level intelligence.
The Case of Forbidden Friendship
Consider, for a moment, Forbidden Friendship, though this train probably starts some time before that.
In The Forbidden Friendship, Hiccup initially tries to buy Toothless’ friendship with a measly offering of one (1) fish. Throughout the scene, Hiccup tries to get closer to Toothless, but the dragon isn’t quite thrilled with the prospect. And not only thrilled: he was outright annoyed. But Hiccup stubbornly persisted, until this happens:
At this point, Toothless knows that Hiccup is very interested in him. He is also perceptive to notice that of all places that Hiccup could choose unnoticed, he went straight for the tail.
Hmm, interesting, thought Toothless to himself as he noticed the young lad interested in his tail specifically. Thus, he decided to check what exactly the little annoyance is looking for.
In all fairness, though, Toothless probably already knew that half his tail is missing by that point. He not knowing that is a very hard sell at this point in the movie. It’s much more likely that Toothless knew that half his tail is missing, but:
he didn’t make the connection between missing tail and his inability to fly until he examined the tail again, or
he made the connection, but hoped he will manage to overcome it, or
he made the connection, but hoped the tail will grow back
he made the connection, knew he can’t overcome it, knew the tail won’t grow back, but he just wondered what the hell does Hiccup see on his tail
The last three seem to make decent amount of sense. After all, the power of denial is immense. It’s not obvious which of the three options is correct, but it’s pretty obvious that this is when Toothless realized that his hopes are unrealistic (assuming b or c are correct) and that there must be a reason for Hiccup’s interest (any option). If you think that the latter option doesn’t require human-level intelligence, just let me remind you that there’s many people who wouldn’t be able to deduce that.
It’s also pretty obvious that during that while inspecting his tail, he realized that — like it or not — Hiccup is the only chance for survival he has.
And just like that, Toothless goes and tries to do something to ensure Hiccup will keep bringing him food, maybe even something extra.
The Case of Spinning Astrid
The second, even better example, is the Astrid Goes for a Spin scene and the events leading to it. To refresh your memory, that’s the point in the movie where Astrid discovers that Toothless exists and then runs off to warn the village.
Toothless seemingly doesn’t think Astrid running away is a big issue at first, but after some encouragement from Hiccup, he does catch himself an Astrid. This tells us that again, Toothless must understand human language, because that’s the only way he could have known that Hiccup wants him to catch Astrid and coerce her into not tattiling. It’s also very likely (although movie doesn’t provide solid evidence in favour of this) that Toothless actually understood the ramifications of Astrid tattling on them after a brief off-screen chatter with Hiccup.
In any case, Toothless manages to catch Astrid. Hiccup convinces Astrid to give them a chance. Hiccup tells Toothless to give Astrid a gentle ride to the ground. And Toothless …
… Toothless wilfully ignores Hiccup’s words.
Yes, Toothless waterboarded Astrid because he wanted to. It goes against what Hiccup had told him to do, and it ends when Astrid apologizes. While her apology was nothing too specific (🔊 note — this webm has sound):
we can deduce that Toothless very, very likely didn’t go against Hiccup’s words to make Astrid apologize for the axe thingy, but did his best to coerce into not saying a word to the tribe. This is suggested by the lack of Toothless’ eagerness to chase Astrid initially: Toothless seems to see Astrid taking off as a victory enough. She came at him with axe, she ran away, job d o n e:
This suggests that Hiccup did or said something off-screen which made Toothless realize the true danger of Astrid tattling on the tribe — an obvious sign of intelligence. Then, he ignored Toothless in favour of his own plan to keep Astrid quiet, which likewise indicates intelligence (and not just intelligence: it’s clear that Toothless both has his own agency and can plan at least to some extent).
All in all, preliminary data shows that Toothless is indeed smart enough in order to plan the entire thing.
What does the movie say?
Now that we know that Toothless is smart enough to have reacted to Astrid’s words and that mind control bit is a bunch of bullshit, that surely confirms the theory that Hiccup did, in fact, react to Astrid?
The movie makes that pretty clear (🔊):
So what happened?
Let’s take a closer look. When Toothless hears the Green Death’s beacon, he becomes afraid. And that’s probably exactly why Toothless took Hiccup to see her. Because he recognizes the danger she presents to Berk, and because he’s very concerned for Hiccup’s well being (if you need evidence that Toothless cares for Hiccup’s well-being, see his Leeroy Jenkins impression during Hiccup’s final exam the next day).
Toothless wasn’t using Hiccup as his personal Death Note. The trip to Green Death was a warning.
And when Toothless ignores Hiccup’s plea to return back to Berk here:
… he doesn’t do that because he was under Green Death’s influence. He refused because he deemed it important for Hiccup to see that.
Whether this means Toothless betrayed Green Death or not depends on your definition of betrayal.
Toothless does seem to exibit near- or even human-level intelligence
Toothless wasn’t mind-controlled by Green Death into visiting the nest
Toothless likewise didn’t show Astrid and Hiccup the nest because he wanted to submit his suggestion to Astrid’s “you’ll have to kill a dragon.”
Toothless showed Hiccup and Astrid the way because he knew that Green Death was a danger to Berk and wanted to warn them.
Yet another day, yet another post about stuff going wrong. This time, I’ve got a bug report that “videos are jumping around” on Facebook and some other pages. I tried to verify the problem … and everything worked fine for me. Then I decided to boot up Windows and there it was — the problem as described. So nice — we have a problem that happens on some operating systems and doesn’t on others, even though that shouldn’t be the case in theory.
But eventually, the issue was reproduced and that’s all that matters. The issue appears very familiar — it has been observed on reddit before.
A video and a player
In a very ELI5 way, every webpage is made out of a bunch of rectangles (layers, elements), one within another. In order to properly crop a video, we must know which of these elements is actually the player (‘player’ element is to our video what picture frame is to a picture), and we need to know which element is the player element. Picking the wrong element can result in extension cropping to little, too much, or moving the video out of the picture altogether.
We can’t just assume that the first element above the video is a player, either: sometimes sites put addiitonal elements between the two. This is why we need ‘guess’ the player element by looking at the size.
Side note: not all extensions use that approach. Some seem to just assume you use a 21:9 monitor and slap a ‘enlarge this element by 1.3’ on the video element. Great and foolproof strategy for fullscreen. Less great for youtube’s theater mode, twitch with chat opened at the side, or non-fullscreen Netflix.
Legacy and technical debt
The code for determining which element is the player element has some weird quirks thanks to the history of the addon. Most notably, the extension used to work by determining how tall and how wide the video should be back in the day when it was only focused on Youtube and Netflix. This method has a few drawbacks, with most notable ones being:
If you ask browser to tell you the size of the video, it’ll tell you the dimensions you specified
It worked for youtube and netflix, but not for everything else
In general, we can assume that initial size of the player will be exactly as wide as the video or exactly as tall as the video. However, since we actually changed the size of the video (as opposed to telling browser to just enlarge the video by some factor), we couldn’t check for that as if the video was cropped, browser would tell us the post-crop size (and post-crop size is useless for that purpose). Some wonky code was written to deal with this issue and it worked well enough for Youtube and Netflix and sometimes even other sites. However, said code is — in retrospect — pretty bad. Looking at it invokes a few questions that every programmer sometimes asks themselves: “the hell was I trying to do with this shit” and “how the fuck did this even work at all?”
Due to problems with #2, a better solution to resizing the video needed to be implemented, and eventually it was in the form of transform: scale(x,y). Using this to crop video (as opposed to modifying width and height attributes of the video) has some nifty advantages: it’s possible to get the size of the video without taking transform into account. This allows us to rewrite the player detect loop in a way that will correctly detect the player element.
Dealing with duplicates
Another thing worth addressing is “duplicates” — that is, what happens when more than one element on our way from video element to the root of the page has the same size. I haven’t figured out what to do in this case, since the correctness of picking innermost over outermost element for player may differ from site to site. In absence of better options, I decided to score every element that could be our player. Rules of the game:
Every element that matches our criteria gets 100 base points
Elements with 'player' in their ID get 75 bonus points
Elements with 'player' in their classlist get 50 bonus points
The farther the element is from our video, the more penalty points it gets. First match gets 0 penalty points, second gets one, third gets two and so on.
I haven’t had the chance to test this thoroughly, so results may vary.