X39.io
A fancy world of magic, unicorns and a raging lazy dev

Rant - Lets talk about journalctl ....
by X39


Background

I am currently running a dual-boot system with Windows and NixOS ... The NixOS part however, keeps on getting a kernel panic every other minute and diagnosing it until now is the hell at best.

Actual Rant

Logs are nice. They are the effective go-to choice for pretty much any tool that emits some behavior that might or might not be either complex (what happened logs), error prune (whats the cause log) or simply tell you who connected and who did not.

With this settled, lets decide who logs are for? Well, logs are for you, the stupid human, who does not understand what your PC, your Tool or whatever is doing. All fine and gold still here.

But what human scum, what 20/10 idiot, what duchebag decided to make logs a binary format and that only, forcing literally everybody to use that frking "format" that everybody settled ages ago: TEXT

No, lets make everything complicated to read by telling everyone how "easy" and "nice" it now is ...

1. Making something binary is not making it simpler

That is simply a fact. If i present you a huffman encoded version of moby dick with a fancy command line tool to read it from, you would be pretty disappointed right? But if i gave you a txt file with literally THE SAME contents, that you can open on ANY OS using ANY FUCKING TEXT EDITOR, you would be quite fine right?

So why then even CHANGE the fucking thing in the first place?

To get extra info into it? Use XML. Human-Readable and, this probably will blow your mind, you can add extra info to every method.

Was the idea to preserve error codes? News everyone: { "[ERROR]", "[INFO]", "[IDIOT]" } is something one can write in any text file. Bonus points: If well-formed, this is even a valid way to add extra info, so XML not even needs to be chosen either.

To just be different? Well done, that is one cookie you get. You are different to the annoiance.

2. Using the one tool to rule them all is a horrible decision

Lets assume, i only have a broken linux installation at hand, the default LIVE-CD one gets from the interwebs is also borked to hell and crashes immediatly. Now you wanna know what component causes the crash. You only have your handy-dandy android phone at hands and export the whole error logs to it. Now what will you see? GARBAGE is what you will see. An utterly useless binarized format just for the sake of having one

3. The solution to keeping the tool

I know, the following will be hard to swallow for anybody ... but parsing text files is trivial. It takes longer then reading binary formats, yes, but you are not reading the logs for the lulz on a regular basis. You read them because something happened and you want to diagnose it. So lets say, we want to keep the tool that is journalctl but without the idiotic binary file required for it. How would we go onto this?

Simple: Instead of reading the fucking binary format, READ A TEXT FORMAT. It is AS trivial, and has the added benefit of one being able to read it outside of that ugly command line crap. It is readable on ANY OS with ANY text editor and so on. And IF one wants to still filter stuff out, it can be done the same way as before. Sounds crazy, i know. But guess what: IT WORKS.

Epilog

So ... how to progress from here? Chances are, you are at the same point where you are, being angry at that human trash who came up with the idea (before somebody reaches out to me about how good of a person he/she/the group and what intention they had, i did not read into it and come 100% from an angry users experience in diagnosing failure of his fucking system) to binarize that shit.

The only solution is to choke the pill, somehow get a full export into a text file, which is horrible formatted thanks to the "optimized output" (ohh and before you even attempt it: journalctl > my_readbale_log.txt will not work because probably the same idiots thought "why make it easy to export it to txt. It should be hard to read the log in a users-pleasing way" and don't trust those idiots in the web saying "ohh do it anyways because it should work" ... it does not, believe me, i literally used journalctl -b -1 > ~/journal.log and, surprise surprise, i got an empty log file with the fancy size of 0bytes)

So good luck on diagnosing your system with journalctl. You might even be able to use regex, in your head, to solve all your problems at a glance. I personally will just wipe the Linux partition and avoid any Linux running systemd like it offered me a free buttfuck with a horsecock. Prooves once again, why i still stick to windows ... Gaming works perfectly fine and literally everything else too. And once i have a problem, i just wipe the whole system and be done with it. Linux? Nowadays: Have a kernel panic, not even get logs because kernel panic reasons are not displayed per default anymore (can still remember those fancy days where i got that sweet message of "ohh yeah ... the USB driver for XY crashed unexpected" thanks to some broken device i had attached to it, allowing me to (and stick with me here again) DIAGNOSE the reason easily. It was a blessing and not that mess like with windows where i had to google after the reason and dig in system logs), after enabling them not being able to find them (thanks to some weird configuration quirks that are not set up correctly, as just enabling coredumps is no longer enough), reading through the logs to find the cause not being possible (hey, windows bluescreens are nowadays easier to debug then linux ones) and the internet being full of uninformative "ohh this app crashes" when you try to get anything related to "kernel panic", "system crash" or whatever.

So long story short: Stay away from linux. It is a horrible experience nowadays, unless you have a setup that already works ... and it is a shame that it changed to this mess it is nowadays.

C++ - Fixing "Nested Templates" linker errors
by X39


So today i had to fix something rather ugly, a linker error caused by a missing template specialization.

The problem?

template<typename T>
data to_data(T t);

class value
{
        data m_data;
    public:
        // ...
        template<typename T>
        value(std::shared_ptr<T> d) : m_data(to_data<T>(d)) { }
};

This fancy snippet here. Looks harmless, two templates, one not implemented one is. All fine and gold right? Well ... only if you always provide the upper somewhere. As it comes, my to_data<T> template implementations are rather specializations like eg.

template<>
data to_data<std::string>(std::string t) { ... }

again, all fine up until the point where you got a huge code base and actually used something bad ... i ... missed something ...

// LNK ERROR because the linker tried to resolve the following
template<> data to_data<char*>(char* t);

now, normally you could just throw in the implementation and call it a day. But with char* things are different ... a dangerous thing, because this might be a potential memory leak. So how do you find the cause of this ugly thing?

Well, rather simple! You add a specialization for the "calling" template until you reach the point where you are in a non-template method BUT you do not provide code for it, forcing a linker error.

for me, thhe following changed:

class value
{
        data m_data;
    public:
        // ...
        template<typename T>
        value(std::shared_ptr<T> d) : m_data(to_data<T>(d)) { }

        // Add some PEPPER to fix the linker error
        template<>
        value(char* d);
};

Hitting build, this showed me that the actual cause of this linker issue was callextension_string_string which did indeed passed a raw char* into this fancy method. But luckily no memory leak.

TL;DR:

  1. Add Template Specialization without code until you reach the place that explains the linker error to you
  2. No more complains

C++ - Working around corners
by X39


So as some of you might know, as of now it is not possible to create conversion operators that are not part of the corresponding class.

This causes ugly situations, where one has one class, and eg. std::string, both not editible by yourself for whatever reason i might add, and needs to add a conversion operator simply because value(std::string) is cumbersome and will not work for all cases (easy transformation using <algorithm> for example).

For all of those, i got some very basic solution here:

    #include <memory>
    #include <string>
    #include <iostream>

    class data
    {
        public:
            virtual std::string to_string() const = 0;
    };
    class value
    {
        private:
            std::shared_ptr<data> m_data;
        public:
            value() : m_data() {}
            value(std::shared_ptr<data> data) : m_data(data) {}
            template<typename T>
            value(T t) : m_data(to_data(t)) {}
            std::shared_ptr<data> get() { return m_data; }
    };

    class d_string : public data
    {
        private:
            std::string m_value;
        public:
            d_string(std::string s) : m_value(s) {}
            virtual std::string to_string() const override { return m_value; }
    };

    inline std::shared_ptr<data>& to_data(std::shared_ptr<data>& input, std::string str)
    {
        input = std::make_shared<d_string>(str);
        return input;
    }

    int main(int argc, char** argv)
    {
        value val;
        val = std::string("test");

        std::cout << val.get()->to_string() << std::endl;
    }

This fancy "hack" will output test as expected and work on all major compilers :)

It works because whilst conversion operators might not be possible, operator overloading (like the << operator we abuse here) is!

Let's talk VR (again)
by X39


So ... i got myself an Index.

It was 1079€ and in the end, this would have been the nono for me (paying a grand on a VR headset? no thank you!). However, thanks to regular, wasteful spendings in DotA 2 and Counter-Strike: Global Offensive cosmetics, i was able to get the price down to 607€. Still expensive, but kinda okay.

I played with it now for roughly two weeks and ... it is awesome. Surprise surprise! I KNOW ... now to the part, that connects to the previous posting i made: What do my friends think about it?

VR is great!

I usually showed them two games: Pavlov VR and Beatsaber. Both, i am playing the most right now. The result was always the same:

  • ultimate joy
  • happy faces
  • FUN

My sister, being at the police, had way more fun in Pavlov VR (and pretty much hit as good as i did with the rifles but god damned ... pistols? Literally every shot a hit at the spot she wanted without even thinking for a moment) then i expected at first. A friend of mine played beatsaber for half an hour and got so exhausted that he had to sit down afterwards just to grasp some air again. Another friend wanted to play skyrim, just for us to find out that my modding efforts made the game unplayable (after 2 days i fixed it finally ... save file btw. still broken but at least it starts again). But in the end, all of them asked the same question: How much?

How much is the fish?

Telling all of them the price, made that happy laughter on their face go dark in a blink of an eye... and i only told them what the full kit i bought would cost, not what the beefy PC add to that price. I also mentioned briefly the Oculus All-In-One-Inside-Out Quest Headset, that sells for 450€ currently... still too much.

And, this is essentially why: VR is too expensive for what is has to offer. My usual VR sessions are ... what? Half an hour per day? And i dropped a fortune on a headset that most of the time will gather dust and nothing more. Obviously, i also get other usages out of that headset (i yet have to look into the development side of things) so ... i kinda could argue the pricetag. But normal CONSUMERS like they are? 450€ is a lot of money... Fucking hell, one of my friends got his first car for that price!

Conclusion

The average VR Joe now might say "What gives!?" but to you i want to give you these lil hints:

  • Unless you play no MP games, you will need other players.
  • Indie Titles, lets face it, are shit. They do provide fun ... but they lack a lot of polish most of the times.
  • VR will not settle on one control scheme unless it is mandatory for game developers (small studio asks big company? Who cares?).
  • Enthusiasts do not pay rent.

Long story short: VR is to expensive and needs to drop below 200€ in total for most of those i showed the headset to, to even consider it. SDE does not matters here btw. but the cable was a concern for most of them. Setup is also a concern, so inside out tracking for mass market appeal will it be.

Why VR in it's current form, never will get the "huge hit"
by X39


There is one fairly simple reason: It is way to expensive. Especially in the EU.

Just as some example: The HTC Vive Pro Starter kit (not even the full) sits at a whopping 1199€ (1359,28 USD) in the EU and "just" 968,53€ (1098 USD) in the US. Both prices are way too high, preventing the tech to actually settle with the non-fanboy user. And i do mean fanboy here! Most people on the internet "defending" the high price simply say "just save up some money and you will be fine.". Thing is: At theese prices, people buy cars. There is simply no way the average consumer, only slowly even being able to afford a computer beefy enough to even run VR (especially with the alltime high of graphics cars prices) will not throw out a top-tier graphics card (at time of writing, RTC 2080ti sits at 1100€ in the EU) for "just" some piece of tech he barely ever will use.

And here is the problem: VR is "just" a piece of input-device bundled with a special gimmick, Virtual Reality. Now, people defending VR may say "it is not ment for everyone" but this is the thing they fail to realize. With VR not being for everyone, VR will fail. The entry barrier (the beefy PC) already is high enough that most people out there simply cannot overcome it. But even if the entry barrier is conquered, there now is the problem of the absurd price point.

Now, smart users may point out that there is an alternative, the non-pro HTC Vive. And you are correct, kind of... See, the first generation VR can hardly be sold to anybody who wants to play games casually. It is not comfortable (like most Gen1 VR headsets), has a horrible resolution causing SDE and reading text with it is a nightmare kind of.

All this, makes those headsets suited for enthusiasts, but not the average John Doe who just wants to enjoy playing some games in VR.

This is where Gen2 VR failed miserable: The Price should have gone DOWN and the SDE should have been mostly eliminated. Thing is, price doubled. That is bad. It is literally as simple as this.

Want VR to succeed? Put the headsets into a more "sweetspot" like thing, that does not requires people to save their earnings of a whole decade on theese things.

Want VR to fail? Then leave the price way too high, making the headset itself as expensive as the computer you just purchased to use one of those.

Inkscape - Good Software, Horrible Userexperience
by X39


I installed Inkscape like ages ago to do small edits on some SVG i had. It worked pretty damn good besides ... some problems...

The whole software is not designed so that some newcommer could pick it up easy. Ugly nested menus, way to specific snap options (which is theoretically good, but only for those who use inkscape on a regular basis! For EVERY other person, this is making things confusing AF, especially because not even all options can be put on at a single place) and generally a feel of lacking features due to them being so well hidden in nested menus or wrong terms that somebody not familiar will simply not find it.

Want an example of how hard this can be?

Microsoft provides the icon pack of visual studio for "free" but it has some lacking icons (eg. Tag has no NewTag variant)

Now, to edit this, you can use inkscape and join those two together so that they form a new 16x16 image

sounds easy right?

well ... it is not that simple afterall, but lets walk through this step as somebody new.

  1. You will pull both items into the current page
  2. You try to position them correctly whilst noticing that nothing snaps
  3. You search the snapping options (not knowing where they are, you ignore the righthand side as in most software, such options are at top whilst tools are left and righthand is current selected properties etc.) but cannot find them
  4. You google, to see a tutorial to do that and think "great"
  5. You try to position them correctly and everything still is not snapping
  6. You google again just to find out that those snapping options in the document properties are not all snapping options and enable snapping in the right bar for nodes too
  7. You position them correctly and export it
  8. You notice that the whole exported path is not usable because for some reason it is not in the right size and positoon
  9. You google how to change the page size
  10. You fiddle around a few mins until you got it to 16x16 px
  11. You export again, still not as it is displayed in inkscape
  12. You check the object just to see that its bounds are outside of the page area (and those got exported too)
  13. You try to find a way to crop the object to the visible page
  14. Few minutes later you realize that there is no inbuilt way to crop to the page (but the other way around which are the first few google results that will popup)
  15. Try to use the rectangle tool to create a rectangle at page size
  16. Try again because nothing appeared when you first tried
  17. Repeat step (16) up to the point where you try to just select everything
  18. Realize that you now have a ton of objects in your page
  19. Try to find some sort of object explorer, find none and google
  20. Google solution is not working, search icons on the right to find the treeview explorer
  21. Removing stuff from the explorer is not possible
  22. Write a rant
  23. Open everything in GIMP to do the same
  24. Be done in 20 minutes instead of 2 weeks of learning inkscape fully

In the end, Inkscape just feels bad!

There is a science field in the IT that is called Human-Computer-Interaction and everytime i think about giving inkscape a try, i notice that the whole software is not build in any way to suit to the average human.

It just feels like some developer put some option somewhere without any actual concept of grouping or awareness of other software available on the market.

I am fairly sure that experienced users can make good use of Inkscape, but as somebody who is not mainly doing such things but rather utilizing (or some may call it abusing) the tool to crop, manipulate etc. existing SVG/XAML paths the whole software feels bloated and not thought through.

[BotW] Seriously Nintendo fanboys ... the fuck is it with you?
by X39


Somebody not liking your game as much as you do through your fanboy glasses does not means they hate it...

they just do not use your fanboy-glasses and see the problems which also exist.

Example?

BotW is a good and solid game. It makes fun, got a beautiful world etc. etc.

However!!! It has design flaws which are unavoidable and probably annoyed most of the people playing the game.

Want examples?

The weapon durability system is horseshit... if weapons would "just" break and you could repair them if you bring them to a smith, probably ppl would be more ok with it. At last, it is a neat addition but executed horrible in a way that annoys and is not an addition to the gameplay.

Cooking is fun, but not in BotW. Yes it is neat for the first few times you do it but then you notice a big bummer: You lack a ton of quality of life (from now just refered to as QOL) features. Starting at the simplest: you cannot easily repeat your last cooking or make a bunch with the same. Just crafting eg. a potion feels cumbersome due to you being in need of having the right stuff in your inventory (which is fine) but you cannot see if you even got the right category of items. Due to the interface design, it simply does not feels right when you continue playing and need to cook to get health items, potions etc. it feels like you waste your time as this could be more simple. A simple book for example that opens up and allows to recraft anything you like. Picking ingredients would still be simple, but should be limited to what actually can be used in the current recipe

Motion Control shrines are just awful! It does not feels right to be in need of precise movement when you always can 100% freely move and rotate your object. They feel completly unnatural and anybody thinking they are "nice addition" is right, just that the execution is so wrong. Examples? There are shrines where you have to move a hammer with motion controls. So far so good right? But the hammer just has to swing from the left to the right, hitting a large ball. This however is deadly hard simply because the moment you start moving your joycon, the game moves the hammer. When you then rotate the hammer, the ball will fall off the ledge simply because your hammer is rotated slightly. This feels awful and youre happy when youre done with it. No challenge just pure anger about a broken mechanic.

The defensive mechanics like Shield Parry or jump-avoid are also completly horrible. The timing spot is way to short to get it off reliably, the enemies do not announce their attacks good enough to visually feel it, ... There is simply so much wrong with this mechanic... not to mention that the moment there is more then one enemy, you are simply fucked because all of theese things will not work anymore. So even if you by magic sit through hours to learn to parry correctly, it is no usable skill simply because the enemy behind you will just straigth hit you without a chance to parry or actually see that he is there.

could continue days with this! And it is awful that you fanboys do not want to notice this.

Do theese things hinder me from playing the game? Fuck no. But they exist! Everybody touching the game will experience them, everybody will get annoyed by them (you too) and everybody normal will not enjoy theese things as much as they could.

Theese are the reasons, why BotW should not be even considered as a Game of the Year ...

but they also do not make the game a bad one ... it is just not a perfect game.

In case somebody is in need of Full-Width TreeViewItem background highlighting
by X39


The Style:


                    <Style x:Key="FullRowTVI" TargetType="{x:Type TreeViewItem}">
                        <Setter Property="Background" Value="Transparent"/>
                        <Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
                        <Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
                        <Setter Property="Padding" Value="1,0,0,0"/>
                        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type TreeViewItem}">
                                    <ControlTemplate.Resources>
                                        <ss:StaggeredThicknessConverter Left="True" Length="19" x:Key="TVIStaggeredThicknessConverter" EndType="{x:Type TreeView}"/>
                                    </ControlTemplate.Resources>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition MinWidth="19" Width="Auto"/>
                                            <ColumnDefinition Width="Auto"/>
                                            <ColumnDefinition Width="*"/>
                                        </Grid.ColumnDefinitions>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="Auto"/>
                                            <RowDefinition/>
                                        </Grid.RowDefinitions>
                                        <Border Name="Bd" Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="0" Padding="0" Grid.ColumnSpan="3">
                                            <Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
                                        </Border>
                                        <ToggleButton x:Name="Expander" IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" Margin="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}, Converter={StaticResource TVIStaggeredThicknessConverter}}">
                                            <ToggleButton.Style>
                                                <Style TargetType="ToggleButton">
                                                    <Setter Property="Focusable" Value="False"/>
                                                    <Setter Property="Template">
                                                        <Setter.Value>
                                                            <ControlTemplate TargetType="ToggleButton">
                                                                <Grid Width="15" Height="13" Background="Transparent">
                                                                    <Path x:Name="ExpandPath" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="1,1,1,1" Fill="#444" Data="M 4 0 L 8 4 L 4 8 Z"/>
                                                                </Grid>
                                                                <ControlTemplate.Triggers>
                                                                    <Trigger Property="IsChecked" Value="True">
                                                                        <Setter Property="Data" TargetName="ExpandPath" Value="M 0 4 L 8 4 L 4 8 Z"/>
                                                                    </Trigger>
                                                                </ControlTemplate.Triggers>
                                                            </ControlTemplate>
                                                        </Setter.Value>
                                                    </Setter>
                                                </Style>
                                            </ToggleButton.Style>
                                        </ToggleButton>
                                        <Border Grid.Column="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
                                            <ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
                                        </Border>
                                        <ItemsPresenter x:Name="ItemsHost" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3"/>
                                    </Grid>
                                    <ControlTemplate.Triggers>
                                        <Trigger Property="IsExpanded" Value="false">
                                            <Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/>
                                        </Trigger>
                                        <Trigger Property="HasItems" Value="false">
                                            <Setter TargetName="Expander" Property="Visibility" Value="Hidden"/>
                                        </Trigger>
                                        <MultiTrigger>
                                            <MultiTrigger.Conditions>
                                                <Condition Property="HasHeader" Value="false"/>
                                                <Condition Property="Width" Value="Auto"/>
                                            </MultiTrigger.Conditions>
                                            <Setter TargetName="PART_Header" Property="MinWidth" Value="75"/>
                                        </MultiTrigger>
                                        <MultiTrigger>
                                            <MultiTrigger.Conditions>
                                                <Condition Property="HasHeader" Value="false"/>
                                                <Condition Property="Height" Value="Auto"/>
                                            </MultiTrigger.Conditions>
                                            <Setter TargetName="PART_Header" Property="MinHeight" Value="19"/>
                                        </MultiTrigger>
                                        <Trigger Property="IsSelected" Value="true">
                                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                                        </Trigger>
                                        <MultiTrigger>
                                            <MultiTrigger.Conditions>
                                                <Condition Property="IsSelected" Value="true"/>
                                                <Condition Property="IsSelectionActive" Value="false"/>
                                            </MultiTrigger.Conditions>
                                            <Setter TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                                        </MultiTrigger>
                                        <Trigger Property="IsEnabled" Value="false">
                                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                    <Style TargetType="{x:Type TreeView}">
                        <Setter Property="ItemContainerStyle" Value="{StaticResource FullRowTVI}"/>
                    </Style>```

The Converter:

using System; using System.Globalization; using System.Windows; using System.Windows.Data; using System.Windows.Media;

/// <summary>
/// Adds up the left margin of the upper visual tree up to the point where the <see cref="Type"/> is no longer matching starting at 0.
/// Expects the <see cref="DependencyObject"/> as value, will simply return value if passed value is not of <see cref="Type"/> <see cref="DependencyObject"/>
/// <see cref="UnsetOnNullConverter.ConvertBack(Object, Type, Object, CultureInfo)"/> will always return <see cref="System.Windows.DependencyProperty.UnsetValue"/>.
/// </summary>
public class StaggeredThicknessConverter : IValueConverter
{
    /// <summary>
    /// The length that should be added each time.
    /// </summary>
    public double Length { get; set; }

    /// <summary>
    /// Type that will be searched for. Continues to count until null or this.
    /// If not set, will count until different parent is encountered.
    /// </summary>
    public Type EndType { get; set; }

    /// <summary>
    /// Sets wether the top part of <see cref="Thickness"/> should be affected.
    /// </summary>
    public bool Top { get; set; }
    /// <summary>
    /// Sets wether the bottom part of <see cref="Thickness"/> should be affected.
    /// </summary>
    public bool Bot { get; set; }
    /// <summary>
    /// Sets wether the left part of <see cref="Thickness"/> should be affected.
    /// </summary>
    public bool Left { get; set; }
    /// <summary>
    /// Sets wether the right part of <see cref="Thickness"/> should be affected.
    /// </summary>
    public bool Right { get; set; }

    /// <summary>
    /// Converts a value
    /// </summary>
    /// <param name="value">The value produced by the binding source.</param>
    /// <param name="targetType">The type of the binding target property.</param>
    /// <param name="parameter">The converter parameter to use.</param>
    /// <param name="culture">The culture to use in the converter.</param>
    /// <returns>A converted value. If the method returns null, the valid null value is used.</returns>
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is DependencyObject obj))
        {
            return value;
        }
        var actingtype = obj.GetType();
        Type objtype;
        double leftmargin = 0;

        while((objtype = (obj = VisualTreeHelper.GetParent(obj)).GetType()).IsEquivalentTo(actingtype) || (this.EndType != null && !objtype.IsEquivalentTo(this.EndType)))
        {
            if (objtype.IsEquivalentTo(actingtype))
            {
                leftmargin += this.Length;
            }
        }
        return new Thickness(
            this.Left ? leftmargin : 0,
            this.Top ? leftmargin : 0,
            this.Right ? leftmargin : 0,
            this.Bot ? leftmargin : 0);
    }

    /// <summary>
    /// Converts a value.
    /// </summary>
    /// <param name="value">The value that is produced by the binding target.</param>
    /// <param name="targetType">The type to convert to.</param>
    /// <param name="parameter">The converter parameter to use.</param>
    /// <param name="culture">The culture to use in the converter.</param>
    /// <returns>A converted value. If the method returns null, the valid null value is used.</returns>
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return DependencyProperty.UnsetValue;
    }
}

python bullshittery - today: raw strings
by X39


dir = r"C:\somepath\log\"

results in SyntaxError: EOL while scanning string literal

lovely

Just killed my debian setup by accident ...
by X39


just took 8 hours to restore everything to a state where it was before (ok ... also fixed problems like reverse-dns lookup being wrong etc. :3)

anyways ... everything up running again