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.
And in the follow-up post, they basically stated that Toothless had no other options but to tell dragons to back off even if you ignore the Thotfury issue.
So let’s take a look at why this is wrong, no matter how you slice it.
If Grimmel Killed the Light Fury
… then he wouldn’t live for long. He wouldn’t even live for short. He’d immediately get blasted apart by every single draconid on Berk.
Contrary to the popular belief, the weapon specimen that Grimmel wields in that scene is not an M4A1. It’s a standard-issue medieval ballista. Its fire rate is much, much lower than that of M4A1 and reload times are pretty awful to the point getting more than one shot out of ballista in any given minute would likely be a massive achievement. In any case, once Grimmel fired his first shot, dragons and wyverns would have forever to blast him.
Obviously, we’re going to face a minor issue here. First of all, you can’t damage the dragon-powered quadcopter Grimmel flies on. That problem is easy enough to solve (just grab him and/or knock him off his stand). The other problem is that precision-blasting Grimmel off the quadcopter leaves the dragon-quadcopter without its pilot. This is a slightly awkward situation, because there’s no telling how deathgrippers would react. However, all the possible reactions generally fall into three groups:
They hover in place
They continue moving towards the ships at the unchanged pace
Panic like my XCOM2 squad the moment they catch a whiff of aliens.
Option 1 is ideal and makes rescuing Toothless easy. Option 2 is a bit worse, but still ideal. There’s six death grippers (four on quadcopter + two solo) facing Toothless’s entire army, which means that Toothless would be returned to the island before he made it halfway to the ships.
Option 3 is where things get a bit more problematic. Panicked death grippers translate into problems for the quadcopter. Best case, quadcopter will become unstable. Worst case, death grippers end up knocking themselves out and freefall to their death. The chance of anyone falling to their death is still borderline negligible in this case — if four death grippers were enough to hold the quadcopter in the air, dozens of Berk’s dragons shouldn’t have a problem holding up the damn thing in the air.
If Thotfury gets killed, then it’s game over for Grimmel while team Toothless wins with zero additional causalities.
And if you wondered which of the three options is going to happen if you remove Grimmel from his seat without damaging the quadcopter, here’s your answer. Death grippers continue hovering in place.
The quadcopter only started crashing once Toothless dropped his mixtape.
Could Grimmel be Disposed of Before He Gets a Chance to Kill Thotfury?
Let’s just put things this way. Grimmel doesn’t have eyes on his ass. Have some dragons go around and attack him from the back.
The obvious issue with this plan is that two solo-flying death grippers could notice them and alert Grimmel. That still doesn’t mean Grimmel would get a chance to kill the light fury. The moment he turns around to check what the ruckus behind him is the moment he’s not aiming for the light fury. And the moment he’s not aiming at the light fury (or just being generally distracted) is an opportunity to get rid of him without thotfury getting it.
Would Grimmel Kill the Light Fury
Given everything said so far: lol.
Of course he wouldn’t. Not only would he no longer have a bargaining token for Toothless — once Light Fury is dead, there’s literally no reason for draconids to take it easy on Grimmel; and as first section shows us: Grimmel would get absolutely annihilated in this case.
And the flock wasnt really in danger. The hunters left the cages unlocked bc they thought Hiccup wasnt coming, and Toothless obviously knew he would bc he then immediately commanded the three like, second in command dragons to break out and go fight
What? That’s patently false.
First of all, the trappers didn’t leave the cages unlocked because they thought Hiccup wasn’t coming. They left them unlocked because “plot convenience.”
If you take a closer look at the cages, there’s two things one can notice.
All cages come equipped with bolt locks and nothing else. Nobody is using padlocks or keys, because why would you? Literally no benefit. Keylocks are mighty expensive and time consuming to manufacture and offer no extra security when you’re trying to keep a dragon in the cage.
Doorhandles are also nowhere to be seen, which means that if the door is not locked with the deadbolt lock, it can be easily opened from the inside of the cell.
Let’s take a look at some screencaps:
The second thing worth mentioning is that trappers were, in fact, locking the cages. However, since there was more cages than there was trappers, the trappers couldn’t lock everything at once. I was a bit unfair when I called it a ‘plot convenience’ earlier, I know.
Just because the trappers haven’t managed to lock all the cages in 60 seconds flat, that doesn’t mean they haven’t been locking them.
So that statement is a bunch of ballooney. But then again, this second reblog is so dumb that it barely warrants a response.
Ultrawidify has been seeing some issues with constant aspect ratio readjustments. This post examines and explains why and how these issues happened.
I don’t think I’ve boasted about developing Ultrawidify much on this blog. Maybe I should have, but then again: the audience of this blog is a) people who know me and b) people who use Ultrawidify and were bored enough to click that link in update notes. The point is — you’re all familiar with this extension.
A while back, I’ve noticed an issue on Twitch. It turned out that the video was a bit … well, twitchy. However, the issue seemed to be fairly uncommon, so I made a note in my test videos file and decided to kick the can a little farther down the road. “No big deal, it surely can wait.”
Well, turns out that the issue was a bigger deal than I thought. I’ve recently accidentally visited my facebook feed, and the twitching issue appeared — except worse. I tend to avoid twatter as well, but long story short: I stumbled on a tweet with embedded video.
On the plus side, the issue happens very often (more often than auto-detection interval), and it happens equally often even when the video is paused. Here we get the first (and perhaps the only) bit of good news for the day: auto-detection isn’t to blame — and since there’s exactly one other thing that could cause this behaviour, this means I already know where the problem is.
In Search of the Problem
In order to understand whats and whys of the problem, we have to take a quick look at how Ultrawidify crops the video. It’s very simple: it finds the video element and basically tells the browser: “Make sure the video is this wide and this tall and then enlarge it by this much,” where “this much” is whatever number auto-detection script (or user intervention) spat out. In programmer jargon, that’s called setting style string.
For technical reasons,1Blame the video alignment feature for that! Ultrawidify’s auto-detection will also “correct” the aspect ratio when there’s no need — in cases like this, the video would be enlarged by a factor of 1: same size as before.
This should, in theory, do the job just fine. In practice though, Ultrawidify isn’t the only thing doing that. Some sites will also tell the browser to make the video element that big because something on the page changed (example: switching between normal and theater mode on youtube). This effectively undoes any changes Ultrawidify has made to the page. And we really don’t want that, since that has the potential to uncrop the unnecessary letterbox.
Solution to this problem is easy enough at the first glance: we’re just gonna tell Ultrawidify to watch for sites trying to meddle with the video size. If the website tries to change anything, Ultrawidify will undo that change immediately. I think you can see where this is going.
Yes. Twitter is also watching for anything that would meddle with video sizes. If it detects that something changed how big the video element is supposed to be, it will undo that change.
Developer tools seem to agree with this assessment. In inspector view, video element is blinking like there’s no tomorrow while in the console, Ultrawidify is seen setting the same style string over and over again, and the zoom factor is always one. At this point you may wonder why the twitchy video if ultrawidify sets the zoom factor to one, and the answer is simple: twitter doesn’t.
With Twitter insisting that the video should be zoomed by a factor of 1.005, Ultrawidify wanting a zoom factor of 1, and neither being very keen on letting go. And this spells trouble for us.
In Search of Solution
If the site will just undo our changes, what can we do? Well, it turns out that there’s a way. As it turns out, there’s actually two sorts of CSS styles: author styles — which is CSS defined by the website you’re visiting; and user styles.
Through the magic of user styles, you — the user — have the final say over how the browser will display the site. If Facebook says the background of the page needs to be white, and you have user style that says the page background should be whatever meme is popular this week … well, tough luck Facebook. Nothing can override user styles, which makes them the perfect “fuck you, you’ll do what I tell you” card. We’ll take it.
There’s another piece of good news: you don’t have to define the styles in advance — you can make them up on the spot and tell the browser to use that. WebExtension API allows us to do that. There is a few caveats, though. Besides making up the style, you also have to make up a class name, attach it to the element and hope that the site won’t remove it. If you want to edit your style, you have to throw the old style at the browser and tell it that you want it removed. You also have to create a brand new style, tell the browser to use that.
Fortunately for us, it currently seems that the sites aren’t as thorough with removing “unauthorized” CSS classes from elements as they are with “unauthorized” styles, but that’s all it is for now: a rule of thumb that everyone seems to follow, until someone doesn’t.
Well, that was a fairly easy fix, wasn’t it? After all, it required very little work from us (the user-style injecting is already implemented — as a part of dealing with vimeo and its special snowflakey bullshit). Performance seems comparable to what it was before (albeit a tiny bit slower to react to changes) and all is well.
Further testing reveals that tie twitching image issue was fixed on Twitter, probably on Facebook as well. At least as far as Firefox is concerned.
Because there is one problem with my solution: Chrome doesn’t support tabs.removeCss(), which turns programmatically generated styles into a bunch of unsuspecting fellows checking into a hotel California. Sure, you can insert the style at any time you want, but it can never leave.
Not being able to yank the previous style when adding a new one is problematic the same reason you telling your kids to wear a white t-shirt where your partner told them to wear a red one a minute ago is problematic. It’s also problematic the same reason putting a new highway tolling sticker on your windshield without removing the old one.
Fortunately for us, Chrome takes the “common sense” (and standard-compliant) approach to handling the first problem: in case you have multiple conflicting definitions, it’s gonna respect the last one. This makes it a bit easier to ignore the second problem, though having tens or in the very worst case (frequent aspect ratio changes, frequent resizings of the player and browser window and watching a video in the same time for long periods of time) even hundreds of conflicting styles shouldn’t be too much of a problem.
Now, I could probably spend another day trying to deal with Chrome shittiness and try to invent new workarounds, it’s easier to just sit back, REEEEE at Chrome, use my userbase as a glorified guinea pigs and hope that people who watch youtube without closing/refreshing/opening video in a different tab won’t have their performance degraded.
And that’s a very dangerous game. I should know: the first update where autodetection was introduced had major performance issues if you watched videos in a tab continuously for long amount of time (Chrome was a complete lagfest in ~5 minutes of watching video, while performance in Firefox was somewhat better (and very dependent on your hardware) — just good enough that it escaped my testing. It’s been two years now and my ratings on Chrome Web Store still haven’t recovered.
I’ll still take the gamble though, against my better judgement.
At this point, I suppose it’s time for a PSA:
Public service announcement: Google Chrome is garbage (and so are all other Chromium-based browsers). If you aren’t already, you should really use Firefox instead.
Thanks for coming to my TEDx talk.
The Case of Twitchy Twitch
So the extension stopped doing twitch things on twitter and the new system for resizing video works. But after a quick visit to the out of season April fools joke (a.k.a. the immortal Blizzcon stream from hell) it turns out that the video still appears … twitchy, which means that twitching on twitch was a result of a different problem.
The ball is now back in the court of automatic aspect ratio detection. To be fair, that twitch issue is a problem with autodetection was known for a wihle. After all, most streams don’t have this problem, and the fact that this doesn’t happen when autodetection detects proper letterbox (or that aspect ratio correction works at all — on Twitter, it wouldn’t!) should be plenty of evidence to support that:
This issue smells like a video with a very tiny, nigh unnoticeable black border. Not wide enough for you to notice, but just enough to trigger autodetection. 30 seconds of (local only) defacing, it turns out that our intuition was correct:
Turns out that this is a very interesting edge case, but to understand how things went wrong we’re gonna need a quick crash-course in how auto-detection works. The key steps are:
Start counting rows that contain nothing else than black pixels, from top and bottom to the middle. Each row has a number: top row is row 0, last row has a number that’s one less than the number of rows. Due to performance reasons, we only pick a few columns of each row that we actually check for the presence of a (non-)black pixel.
Remember the first row that contains black pixel (on both ends)
Calculate the height of the black bars
Check whether top and bottom black bar are roughly of equal thickness. We don’t require black bars to match exactly because:
in theory, the “not black bars” portion of the video is not guaranteed to be of even height. In cases like this, the top and bottom black bar could differ by 1px
Calculate and apply aspect ratio. If top and bottom thickness are different, you’ve got two options. Either you over-cut or you end up with a thin black border on either top to bottom. I’m not exactly 100% sure which strategy I’m using: twitch issue suggests I’m using strategy A (overcut), but the previous video suggests black edges are preferable.
That’s the basic idea behind aspect ratio detection, but reality is far more complicated in order to crack down on false positives. This means that we want to avoid unnecessary checks if possible.
Once we determine the correct aspect ratio, we can take a shortcut. Since we know where black bars end and the image begins, we can just check these four rows:
So fine, we save numbers of these four rows. Top outer row is the last row in which we failed to detect a non-black pixel minus a pixel or two for safety, top inner row is the first row in which a non-black pixel was detected plus a pixel or two of safety margin.
There is, of course, a problem. If the top black border is one pixel thick and bottom black border doesn’t exist, the extension would have us check rows that don’t actually exist. Since the rows we need to check don’t exist, we can assume that black bars either don’t exist or are too thin to actually annoy anyone. When that problem happens, the extension resets the aspect ratio back to what it was originally.
Here’s a fun fact tho: this step — saving numbers of the four rows — happened only after extension already corrected aspect ratio. Normally that’s not a problem, because videos don’t have one pixel thick black bars on the top and no black bar at the bottom. They would either have substantial letterbox, or none at all.
In this particular case, though, things were a bit different. Ultrawidify would detect the one pixel letterbox and correct it. Then it would check where the black bar edges are and apply the safety margins. Safety margins would be out of bounds.
Would say Ultrawidify and undo the correction right away, and this cycle would repeat forever (or at least as long as one-pixel and zero-pixel letterbox was present).
Fortunately enough for us, the fix for that is easy enough: we try to save the edge rows, and only issue aspect ratio correction if the result isn’t bogus.