0

I have a Windows 10/Arch Linux dual boot setup. I've configured both systems to use the same Desktop (simply symlinked /home/rawing/Desktop to D:/Users/Rawing/Desktop). It works fine, I have access to all my files on both systems, but I would like to sync the location of the files on the Desktop. For example, if I create a new file on linux and move it to the bottom right corner of the Desktop, Windows will still show the new file in the top left corner, forcing me to relocate the file a 2nd time.

Is there a way to sync the location of the files on the Desktop?

1
  • What you want is not realistic for the reasons CCondard96 listed. – Ramhound Aug 29 '16 at 20:48
3

Icon locations would be stored differently for each OS. You cannot have the location of icons shared across windows and linux.

2
  • Of course I'm aware of that. So I just need to convert the one representation to the other, no? I'm baffled by the confidence with which you say "you cannot have" and all the upvotes, I expected more fighting spirit from people who call themselves SuperUsers... – Aran-Fey Aug 29 '16 at 21:10
  • Sorry. I should have said "cannot EASILY have". I imagine you could have cron tasks and startup scripts that parse the other OS's files that store the icon locations, then modify the files of the current OS. The entire process would be a dirty way of doing it, and could lead to corruption if a script doesn't finish properly (IE it gets stuck in the boot process). So, as Ramhound says, it really isn't realistic. – CConard96 Aug 29 '16 at 21:16
0

I've made some progress that I'd like to share. This isn't a complete, working answer yet, but I've cleared some hurdles.

Python3 script for Linux:

def get_icon_positions_linux():
    desktop= os.path.join(os.path.expanduser('~'), 'Desktop')
    icons= {}

    #try to find out the screen resolution
    resolution= get_screen_resolution()

    for name in os.listdir(desktop):
        path= os.path.join(desktop, name)
        data= subprocess.getoutput("gvfs-info -a 'metadata::nautilus-icon-position' "+shlex.quote(path))
        if not data:
            continue
        match= re.search(r'(\d+),(\d+)\s*$', data)
        x, y= int(match.group(1)), int(match.group(2))
        if resolution is not None:
            x/= resolution[0]
            y/= resolution[1]
        icons[name]= (x,y)

    return icons

def set_icon_positions_linux(icons):
    desktop= os.path.join(os.path.expanduser('~'), 'Desktop')

    for name,(x,y) in icons.items():
        path= os.path.join(desktop, name)
        pos= ','.join(map(str, (x,y)))
        subprocess.call(['gvfs-set-attribute', '-t', 'string', path, 'metadata::nautilus-icon-position', pos])

    #FIXME: refresh desktop

And Windows:

def get_icon_positions_windows():
    # retrieves icon locations from the Windows registry. More info can be found here:
    #    https://superuser.com/questions/625854/where-does-windows-store-icon-positions

    #FIXME: before doing anything, make explorer save the current positions

    import winreg

    icons= {}

    key= winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Microsoft\Windows\Shell\Bags\1\Desktop')

    #find the key with the current screen resolution
    resolution= get_screen_resolution()

    valuename= 'ItemPos'+ 'x'.join(map(str, resolution))
    for index in range(1024):
        value= winreg.EnumValue(key, index)
        if value[0].startswith(valuename):
            break

    #parse the icon locations
    data= value[1]
    #first 16 bytes are junk
    data= data[0x10:]

    format= 'HHIHHH'
    name_offset= struct.calcsize(format)
    while True:
        # What they call "unknown padding" is exactly what we're looking for.
        x, y= struct.unpack_from('II', data) # (x,y) in pixels on the Desktop from the top left corner.
        data= data[8:]

        if len(data)<name_offset:
            break

        size, flags, filesize, date, time, fileattrs= struct.unpack_from(format, data)
        if size==0:
            break

        chunk= data[:size]
        data= data[size:]
        if size>=0x15:
            chunk= chunk[name_offset:]
            name= chunk[:chunk.find(b'\0')].decode()

            icons[name]= (x/resolution[0], y/resolution[1])
    return icons

def set_icon_positions_windows(icons):
    """
    WARNING: This will kill and restart the explorer.exe process!
    """
    import winreg

    key= winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\Microsoft\Windows\Shell\Bags\1\Desktop', 0, winreg.KEY_ALL_ACCESS)

    #find the key with the current screen resolution
    resolution= get_screen_resolution()

    valuename= 'ItemPos'+ 'x'.join(map(str, resolution))
    for index in range(1024):
        value= winreg.EnumValue(key, index)
        if value[0].startswith(valuename):
            valuename= value[0]
            break

    old_data= value[1]
    #first 16 bytes are junk
    new_data= old_data[:0x10]
    old_data= old_data[0x10:]

    format= 'HHIHHH'
    name_offset= struct.calcsize(format)
    while True:
        if len(old_data)<8+name_offset:
            break

        size, flags, filesize, date, time, fileattrs= struct.unpack_from(format, old_data[8:])
        if size==0:
            break

        chunk= old_data[8:8+size]
        if size>=0x15:
            chunk= chunk[name_offset:]
            name= chunk[:chunk.find(b'\0')].decode()

            if name in icons:
                #update the position
                x, y= icons.pop(name)
                new_data+= struct.pack('II', x, y)
                #copy everything else
                new_data+= old_data[8:8+size]
                old_data= old_data[8+size:]
                continue

        #if it's an invalid struct (size<0x15) or we're not supposed to change this file's position, copy the old value
        new_data+= old_data[:8+size]
        old_data= old_data[8+size:]

    #if there are still icons remaining, we must construct their data from scratch
    for name,(x,y) in icons.items():
        name= name.encode()
        if len(name)%2:
            name+= b'\0'
        size= name_offset + len(name) + 4
        chunk= struct.pack('II', x, y)
        chunk+= struct.pack(format, size, 0, 0, 0, 0, 0) #FIXME: set sensible values
        chunk+= name
        chunk+= struct.pack('HH', 0, 0) #FIXME: set sensible values
        new_data+= chunk

    #whatever data remained, add it.
    new_data+= old_data

    winreg.SetValueEx(key, valuename, 0, winreg.REG_BINARY, new_data)

    #restart explorer for changes to take effect
    subprocess.call(['taskkill', '/f', '/im', 'explorer.exe'])
    subprocess.Popen(r'start %WINDIR%\explorer.exe"', shell=True)

The code currently has the following problems:

  1. get_icon_positions_windows doesn't force Windows explorer to save the current icon locations. It creates a snapshot of the last saved locations.
  2. set_icon_positions_linux doesn't refresh the Desktop. You might have to hit F5 for the changes to take effect.

The idea is to use this code to write/load the icon positions to/from a file whenever the user logs off or on. Sadly, I haven't discovered a way to run a command on logout on Windows yet. (See my other question)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.