Custom D.E.O.T. V2.0+/Clockwork OS v0.5 image - With customised DEOT interface, Kernel 5.7, Optional 1400MHz OC, Debian 10 Buster, Retroarch 1.9.0, Mupen64+ plus more! (Current build: 200903)

And of course I end up doing something new tonight AFTER I upload the above image!

Good news! I got the stock settings page items working.
They’re the ones featured in this promotional material on the top right two panels. (I have no idea where that Loading screen comes from - I’ve never seen it!)

Here are the details.

I was right, regarding referenced fonts not lining up.

Initially, what I tried was substituting the referenced font names with the stock font names of 0.5. The DEOT OS uses the Eurostile font a lot, whereas stock 0.5 doesn’t.
Needless to say, the launcher wouldn’t progress if any changes were made.

I didn’t want to touch the /home/cpi/launcher/sys.py/UI/skin_manager.py, since being within the launcher directory, it would get overwritten with the stock version upon updating, which would cause problems re: the launcher not loading.

But realistically, if the skin_manager.py was going to get updated/replaced with stock, chances are the /home/cpi/launcher/Menu/GameShell/10_Settings/ would also get completely overwritten, preventing the launcher freeze happening.

That and the fact that we haven’t seen a launcher update for a while. So I went ahead and modified the skin_manager.py file like this:

# -*- coding: utf-8 -*-

import pygame
import config
import ConfigParser

from util_funcs  import FileExists

class CaseConfigParser(ConfigParser.SafeConfigParser):
    def optionxform(self, optionstr):
        return optionstr

class SkinManager(object):
    """
    _HighColor = pygame.Color(51,166,255) # #33a6ff
    _TextColor = pygame.Color(83,83,83) # #535353
    _FrontColor = pygame.Color(131,199,219) ## light blue,#83c7db
    _URLColor  = pygame.Color(51,166,255) ## blue more #33a6ff
    _LineColor = pygame.Color(169,169,169)  # #a9a9a9
    _TitleBgColor = pygame.Color(228,228,228)  # #e4e4e4
    _ActiveColor = pygame.Color(175,90,0) ## light brown  #af5a00
    """
    
    _Colors = {}
    _Config = None
    _Fonts = {}
    DefaultSkin = "../skin/default"

    def __init__(self):
        self.Init()

    def ConvertToRGB(self,hexstr):
        
        h = hexstr.lstrip('#')
        return tuple(int(h[i:i+2], 16) for i in (0, 2 ,4))
    
    def Init(self):
        if not SkinManager._Colors:
            self.SetColors()
        if not SkinManager._Fonts:
            self.SetFonts()
    
    def SetFonts(self):
        if not pygame.font.get_init():
            pygame.font.init()
            
        skinpath = config.SKIN+"/truetype"
        fonts_path = {}
        fonts_path["varela"]   = "%s/VarelaRound-Regular.ttf" % skinpath
        print(fonts_path["varela"])
        fonts_path["veramono"] = "%s/VeraMono.ttf" % skinpath
        fonts_path["noto"]     = "%s/NotoSansMono-Regular.ttf" % skinpath
        fonts_path["notocjk"]     = "%s/NotoSansCJK-Regular.ttf" % skinpath
     	fonts_path["Eurostile"] = "%s/EurostileMN-Medium.pfb.ttf" %skinpath
        fonts_path["EurostileBold"] = "%s/EurostileMN-ExtendedBold.pfb.ttf" % skinpath
        
        
        self._Fonts["EurostileBold13"]  = pygame.font.Font(fonts_path["EurostileBold"],13)        
        self._Fonts["EurostileBold30"]  = pygame.font.Font(fonts_path["EurostileBold"],30)
        
        for i in range(10,29):
          self._Fonts["varela%d"%i] = pygame.font.Font(fonts_path["Eurostile"],i)
          
        self._Fonts["Eurostile34"] = pygame.font.Font(fonts_path["Eurostile"],34)
        self._Fonts["Eurostile40"] = pygame.font.Font(fonts_path["Eurostile"],40)
        self._Fonts["Eurostile120"] = pygame.font.Font(fonts_path["Eurostile"],120)
        

        for i in range(10,29):
          self._Fonts["varela%d"%i] = pygame.font.Font(fonts_path["varela"],i)
          
        self._Fonts["varela34"] = pygame.font.Font(fonts_path["varela"],34)
        self._Fonts["varela40"] = pygame.font.Font(fonts_path["varela"],40)
        self._Fonts["varela120"] = pygame.font.Font(fonts_path["varela"],120)
        
        for i in range(10,26):
            self._Fonts["veramono%d"%i] = pygame.font.Font(fonts_path["veramono"],i)
        
        for i in range(10,28):
            self._Fonts["notosansmono%d"%i] = pygame.font.Font(fonts_path["noto"],i)

        for i in range(10,28):
            self._Fonts["notosanscjk%d"%i] = pygame.font.Font(fonts_path["notocjk"],i)
    
        self._Fonts["arial"] = pygame.font.SysFont("arial",16)
        
    def SetColors(self):
        Colors = {}
        Colors["High"] = pygame.Color(51, 166, 255)
        Colors["Text"] = pygame.Color(83, 83, 83)
        Colors["ReadOnlyText"] = pygame.Color(130,130,130)
        Colors["Front"] = pygame.Color(131, 199, 219)
        Colors["URL"] = pygame.Color(51, 166, 255)
        Colors["Line"] = pygame.Color(169, 169, 169)
        Colors["TitleBg"] = pygame.Color(228, 228, 228)
        Colors["Active"] = pygame.Color(175, 90, 0)
        Colors["Disabled"] = pygame.Color(204, 204, 204)
        Colors["White"] = pygame.Color(255, 255, 255)
        Colors["Black"] = pygame.Color(0, 0, 0)

        SkinManager._Colors = Colors

        self._Config = CaseConfigParser()

        fname = config.SKIN+"/config.ini"

        try:
            self._Config.read(fname)
        except Exception, e:
            print("read skin config.cfg error %s" % str(e))
            return
        else:
            if "Colors" in self._Config.sections():
                colour_opts = self._Config.options("Colors")
#                print(colour_opts)
                for i in SkinManager._Colors:
                    if i in colour_opts:
                        try:
                            SkinManager._Colors[i] = self.ConvertToRGB(
                                self._Config.get("Colors", i))
                        except Exception, e:
                            print("error in ConvertToRGB %s" % str(e))
                            continue
    
    def GiveFont(self,name):
        return SkinManager._Fonts[name]
        
    def GiveColor(self,name):
        if name in SkinManager._Colors:
            return SkinManager._Colors[name]
        else:
            return  pygame.Color(255,0,0)
    
    def GiveIcon(self,orig_file_or_dir): ## return is string,not Surface
        #doing a wrapper for items under /home/cpi/apps/Menu/*, to be like Menu/GameShell/*
        if orig_file_or_dir.startswith("/home/cpi/apps/Menu"):
            orig_file_or_dir = orig_file_or_dir.replace("/home/cpi/apps/Menu/","../Menu/GameShell/")
    
        if orig_file_or_dir.startswith(".."):
            ret  = orig_file_or_dir.replace("..",config.SKIN)
            if FileExists(ret) == False:
                ret = orig_file_or_dir.replace("..",self.DefaultSkin)
        else:
            ret = config.SKIN+"/sys.py/"+orig_file_or_dir
            if FileExists(ret) == False:
                ret = self.DefaultSkin+"/sys.py/"+orig_file_or_dir
    
        if FileExists( ret ):
            return ret
        else:  ## if not existed both in default or custom skin ,return where it is
            return orig_file_or_dir
            
    def GiveWallpaper(self,png_name):
        #first SKIN/wallpapers/xxxx.png
        #second ../skin/default/wallpapers/xxxx.png
        #finnal gameshell/wallpaper/xxxx.png
        #loading.png,seeyou.png,updating.png,gameover.png,desktopbg.png
        wlp = "/wallpaper/"
        if FileExists(config.SKIN+wlp+png_name):
            return config.SKIN+wlp+png_name
        elif FileExists(self.DefaultSkin+wlp+png_name):
            return self.DefaultSkin+wlp+png_name
        else:
            return "gameshell/wallpaper/"+png_name
            
        
            
##global MySkinManager Handler
MySkinManager = None

def InitMySkinManager():
    global MySkinManager
    if MySkinManager == None:
        MySkinManager = SkinManager()
    

InitMySkinManager()

I simply added more font choices, and reference names to the list, without removing anything.
This allows us to now use these customised settings menu items.

  • Airplane
  • Brightness
  • Sound
  • Storage

While I was at it, I removed LauncherGo entirely.
I removed the directory /home/cpi/launcher/Menu/GameShell/10_Settings/LauncherGo/ , and went ahead and removed /home/cpi/launchergo/
And then I commented out the entry within the settings menu like so:

# -*- coding: utf-8 -*- 

import pygame
import sys

from libs.roundrects import aa_round_rect

## local UI import
from UI.constants import Width,Height
from UI.page   import Page,PageSelector
from UI.label  import Label
from UI.util_funcs import midRect,FileExists
from UI.keys_def   import CurKeys, IsKeyStartOrA, IsKeyMenuOrB
from UI.scroller   import ListScroller
from UI.skin_manager import MySkinManager
from UI.lang_manager import MyLangManager
from UI.info_page_selector import InfoPageSelector

from list_item  import ListItem

import myvars

class ListPage(Page):

    _Icons = {}
    _Selector=None
    
    _FootMsg = ["Nav","","","Back","Enter"]
    _MyList = []
    _ListFontObj = MyLangManager.TrFont("varela15")

    _Scroller = None
    
    def __init__(self):
        Page.__init__(self)
        self._Icons = {}
        self._CanvasHWND = None
        self._MyList = []
        
    def Init(self):
        self._PosX = self._Index * self._Screen._Width
        self._Width = self._Screen._Width
        self._Height = self._Screen._Height

        self._CanvasHWND = self._Screen._CanvasHWND

        ps = InfoPageSelector()
        ps._Parent = self
        ps._PosX = 2
        self._Ps = ps
        self._PsIndex = 0
        
        #                ""   pkgname, label
        alist         = [["","Airplane","Airplane Mode"],
                         ["","PowerOptions","Power Options"],
                         ["","Wifi","Wi-Fi"],
                         ["","Bluetooth","Bluetooth"],
                         ["","Sound","Sound  Volume"],
                         ["","Brightness","BackLight Brightness"],
                         ["","Storage",""],
                         ["","Time","Timezone"],
                         ["","Languages","Languages"],
                         ["","Notification","Notification"],
                         ["","Update", ""],
                         ["","Cores", "Retroarch cores manager"],
                         ["","About",  "About"],
                         ["","PowerOFF","Power OFF"],
                         ["","ButtonsLayout","Buttons Layout"],
                         #["","LauncherGo","Switch to LauncherGo"],
                         ["","Lima","GPU Driver Switch"],
                         ["","GateWay","Network Gateway Switch"]]

        start_x  = 0
        start_y  = 0

        
        sys.path.append(myvars.basepath)# add self as import path
        for i,v in enumerate(alist):
            li = ListItem()
            li._Parent = self
            li._PosX   = start_x
            li._PosY   = start_y + i*ListItem._Height
            li._Width  = Width
            li._Fonts["normal"] = self._ListFontObj

            if v[2] != "":
                li.Init(v[2])
            else:
                li.Init(v[1])
            
            #if v[1] == "Wifi" or v[1] == "Sound" or v[1] == "Brightness" or v[1] == "Storage" or v[1] == "Update" or v[1] == "About" or v[1] == "PowerOFF" or v[1] == "HelloWorld":
            if FileExists(myvars.basepath+"/"+ v[1]):
                li._LinkObj = __import__(v[1])
                init_cb   = getattr(li._LinkObj,"Init",None)
                if init_cb != None:
                    if callable(init_cb):
                        li._LinkObj.Init(self._Screen)
                
                self._MyList.append(li)

        self._Scroller = ListScroller()
        self._Scroller._Parent = self
        self._Scroller._PosX = self._Width - 10
        self._Scroller._PosY = 2
        self._Scroller.Init()

    def Click(self):
        cur_li = self._MyList[self._PsIndex]
        if cur_li._LinkObj != None:
            api_cb = getattr(cur_li._LinkObj,"API",None)
            if api_cb != None:
                if callable(api_cb):
                    cur_li._LinkObj.API(self._Screen)

        
    def KeyDown(self,event):
        if IsKeyMenuOrB(event.key):
            self.ReturnToUpLevelPage()
            self._Screen.Draw()
            self._Screen.SwapAndShow()
        
        if event.key == CurKeys["Up"]:
            self.ScrollUp()
            self._Screen.Draw()
            self._Screen.SwapAndShow()
        if event.key == CurKeys["Down"]:
            self.ScrollDown()
            self._Screen.Draw()
            self._Screen.SwapAndShow()
        

        if IsKeyStartOrA(event.key):
            self.Click()
            
    def Draw(self):
        self.ClearCanvas()

        if len(self._MyList) * ListItem._Height > self._Height:
            self._Ps._Width = self._Width - 11
            
            self._Ps.Draw()
            
            for i in self._MyList:
                i.Draw()
        
            self._Scroller.UpdateSize( len(self._MyList)*ListItem._Height, self._PsIndex*ListItem._Height)
            self._Scroller.Draw()
        else:
            self._Ps._Width = self._Width
            self._Ps.Draw()
            for i in self._MyList:
                i.Draw()

We’re getting much closer to the DEOT settings! ALMOST. The settings pages for Airplane and Storage still causes the system to freeze. The Sound and Brightness pages however work beautifully!

Other things that were bugging me were the shapes of certain objects, such as the scroll bar, text highlighter and OSD of the volume bars. These are basically all contained in /home/cpi/launcher/sys.py.UI/ . For the most part, I just substituted the files from the DEOT stock image.
The scroller is controlled by scroller.py
I couldn’t work out the text highlighter. I tried slider.py, to no avail.
The OSD of the volume bars is controller by above_all_patch.py, however substituting it in prevents the launcher from progressing.

Anyway, that’s where I’m at. This could prove to be useful information for someone else wanting to modify the interface. I can possibly provide a script to allow people to try installing this. But really, this is just an aesthetic thing that serves no purpose, and something that could later on down the track break detrimentally. I’m going to test it A LOT!
Here’s a link to the files I replaced, along with backups of the original ones in case you want to try things out for yourself. The files with the asterisk next to them are the problematic items.
https://drive.google.com/open?id=1gf97OddpWn9JjSfM7A3UhxtN0tvYnWvQ