Games-Axmud

 view release on metacpan or  search on metacpan

lib/Games/Axmud/Obj/Desktop.pm  view on Meta::CPAN

# Copyright (C) 2011-2024 A S Lewis
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU
# General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not,
# see <http://www.gnu.org/licenses/>.
#
#
# Games::Axmud::Obj::Desktop
# The main desktop object. Arranges windows on one or more workspaces, each containing one or more
#   workspace grids

{ package Games::Axmud::Obj::Desktop;

    use strict;
    use warnings;
#   use diagnostics;

    use Glib qw(TRUE FALSE);

    our @ISA = qw(Games::Axmud);

    ##################
    # Constructors

    sub new {

        # Called by GA::Client->start
        #
        # Expected arguments
        #   (none besides $class)
        #
        # Return values
        #   'undef' on improper arguments or if a GA::Obj::Desktop object already exists
        #   Blessed reference to the newly-created object on success

        my ($class, $check) = @_;

        # Check for improper arguments
        if (! defined $class || defined $check) {

            return $axmud::CLIENT->writeImproper($class . '->new', @_);
        }

        # Only one desktop object can exist
        if ($axmud::CLIENT->desktopObj) {

            return undef;
        }

        # Setup
        my $self = {
            _objName                    => 'desktop',
            _objClass                   => $class,
            _parentFile                 => undef,       # No parent file object
            _parentWorld                => undef,       # No parent file object
            _privFlag                   => TRUE,        # All IVs are private

            # Widget registries
            # -----------------

            # The GA::Obj::WMCtrl object, based on X11::WMCtrl, which handles multiple workspaces
            #   (on systems that support wmctrl)
            wmCtrlObj                   => undef,

            # Registry hash of all workspace objects which still exist. Workspace objects
            #   (GA::Obj::Workspace) handle a single workspace on the desktop, on which 'grid'
            #   windows can be arranged on a 3-dimensional grid to avoid overlapping (each grid is
            #   handled by a workspace grid object, GA::Obj::WorkspaceGrid)
            # If GA::Client->shareMainWinFlag = TRUE, all sessions share a single 'main' window,
            #   so each session has its own workspace grid object on every workspace. If
            #   GA::CLIENT->shareMainFlag = FALSE, all sessions have their own 'main' window, so
            #   there is only one workspace grid for each workspace, shared by all sessions
            # NB The workspace object corresponding to the workspace from which Axmud was launched
            #   has the number 0, and cannot be deleted. Unlike GA::Client->initWorkspaceHash,
            #   subsequent workspaces don't have to be numbered sequentially (1, 2, 3...) but are
            #   still numbered in the order in which they're created
            # Hash in the form
            #   $workspaceHash{unique_number} = blessed_reference_to_workspace_object
            workspaceHash               => {},
            # Number of workspace objects ever created (used to give each workspace object a unique
            #   number)
            workspaceCount              => 0,
            # For convenience, the 'default' workspace object (whose unique number is 0) is stored
            #   here
            defaultWorkspaceObj         => undef,

            # Registry hash of workspace grid objects which still exist. Each workspace objects
            #   usually has one or more workspace grid objects (or none, if
            #   GA::Obj::Workspace->gridEnableFlag is TRUE)
            # Each workspace grid object either belongs to a single session
            #   (GA::Client->shareMainWinFlag = TRUE), or is shared between all sessions
            #   (GA::CLIENT->shareMainWinFlag = FALSE)
            # A workspace grid is divided up into zones. Zones can't use partial gridblocks so a
            #   zone size of 15x15 is possible, but not 20.5x15
            # Zones can specify that only certain window types can be placed in them, or can specify
            #   that all window types may be placed in them.
            # Hash in the form
            #   $gridHash{unique_number} = blessed_reference_to_workspace_grid_object
            gridHash                    => {},
            # Number of workspace grid objects ever created for this workspace (used to give every
            #   workspace grid object a unique number)
            gridCount                   => 0,

            # Registry hash of all 'grid' windows which still exist. 'grid' windows are any window
            #   specified by GA::Client->constGridWinTypeHash, specifically, windows which can be
            #   placed on a workspace grid (unlike temporary 'free' windows, which are never placed
            #   on a workspace grid)
            # 'Grid' windows are handled by GA::Win::Internal or GA::Win::External objects
            # Hash in the form
            #   $gridWinHash{unique_number} = blessed_reference_to_grid_window_object
            gridWinHash                 => {},
            # Number of 'grid' window objects ever created (used to give each 'grid' window object a
            #   unique number)
            gridWinCount                => 0,

            # Registry hash of all 'free' windows which still exist (except 'dialogue' windows,
            #   which are not stored in any registry because they automatically close with their
            #   parent window; all code in this object assumes that 'free' windows exclude
            #   'dialogue' windows)
            # 'Free' windows are any window specified by GA::Client->constFreeWinTypeHash
            #   (specifically, any temporary window which is never placed on a workspace grid)
            # 'Free' windows are handled by various objects inheriting from GA::Generic::FreeWin
            # Hash in the form
            #   $freeWinHash{unique_number} = blessed_reference_to_free_window_object
            freeWinHash                 => {},
            # Number of 'free' window objects ever created (used to give each 'free' window object a
            #   unique number)
            freeWinCount                => 0,

            # Registry hash of all textview objects (created by all sessions) which still exist.
            #   Textview objects (GA::Obj::TextView) handle a single Gtk3::Textview
            # NB The code is at liberty to create its own Gtk3::TextViews, not handled by textview
            #   objects, if the code doesn't need the full functionality of a textview object. Those
            #   Gtk3::TextViews are not stored here
            # Hash in the form
            #   $textViewHash{unique_number} = blessed_reference_to_textview_object
            textViewHash                => {},
            # Number of textview objects ever created (used to give each textview object a unique
            #   number)
            textViewCount               => 0,

            # Other IVs
            # ---------

            # $self->start will set this flag to FALSE if it's not possible to create workspace
            #   grids at all (because the desktop is too small, etc). If set to TRUE, workspace
            #   grids are only created when both this flag and GA::Client->activateGridFlag are
            #   TRUE
            gridPermitFlag              => TRUE,
            # The number of workspaces that Axmud can potentially use is specified by
            #   GA::Client->initWorkspaceHash. On each workspace allowed, Axmud tests whether a
            #   workspace grid can be placed on it. If that test fails, Axmud takes the following
            #   action:
            #       - If it's the default (first) workspace, the workspace object's
            #           ->gridEnableFlag is set to FALSE. Windows are not arranged on a grid on that
            #           single workspace, and no more workspace objects can be created
            #       - For subsequent workspaces, if the test fails that workspace object is deleted,
            #           and windows can only be opened on previously-created workspaces
            # In either case, if the test fails, this flag is set to FALSE to prevent more
            #   workspaces being created
            newWorkspaceFlag            => TRUE,

            # A list of Gtk3::Gdk::Pixbufs corresponding to the icons stored in the '/icons/win'
            #   sub-directory for 'main' windows
            mainWinIconList             => [],
            # A list of Gtk3::Gdk::Pixbufs for 'map' windows
            mapWinIconList              => [],
            # A list of Gtk3::Gdk::Pixbufs for 'protocol' windows
            protocolWinIconList         => [],
            # A list of Gtk3::Gdk::Pixbufs for 'fixed' windows
            fixedWinIconList            => [],
            # A list of Gtk3::Gdk::Pixbufs for 'custom' windows
            customWinIconList           => [],
            # A list of Gtk3::Gdk::Pixbufs for 'external' windows (the pixbufs are created, but not
            #   used - 'external' windows keep their own icons)
            externalWinIconList         => [],

            # A list of Gtk3::Gdk::Pixbufs for 'viewer' windows
            viewerWinIconList           => [],
            # A list of Gtk3::Gdk::Pixbufs for 'edit' windows
            editWinIconList             => [],
            # A list of Gtk3::Gdk::Pixbufs for 'pref' windows
            prefWinIconList             => [],
            # A list of Gtk3::Gdk::Pixbufs for 'wiz' windows
            wizWinIconList              => [],
            # A list of Gtk3::Gdk::Pixbufs for 'dialogue' windows
            dialogueWinIconList         => [],
            # A list of Gtk3::Gdk::Pixbufs for 'other' windows
            otherWinIconList            => [],
        };

        # Bless the object into existence
        bless $self, $class;

        return $self;
    }

    ##################
    # Methods

    sub start {

        # Called by GA::Client->start immediately after the call to $self->new
        # Sets up pixbufs for all window icons, then sets up workspaces, then creates a spare 'main'
        #   window, which doesn't belong to any session, and which will be re-used by the first
        #   session that opens

lib/Games/Axmud/Obj/Desktop.pm  view on Meta::CPAN

                $axmud::CLIENT->set_mainWin($winObj);
            }
        }

        return 1;
    }

    sub stop {

        # Called by GA::Client->stop
        # Closes any remaining 'internal' windows, restores 'external' windows to their original
        #   size/position, closes any remaining 'free' windows
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Local variables
        my ($workspaceCount, $gridCount, $gridWinCount, $freeWinCount, $textViewCount, $msg);

        # Check for improper arguments
        if (defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->stop', @_);
        }

        # Close down workspace objects (which closes down its workspace grids and windows), youngest
        #   first
        foreach my $workspaceObj (
            sort {$b->number <=> $a->number} ($self->ivValues('workspaceHash'))
        ) {
            if ($workspaceObj->number != 0) {

                $self->del_workspace($workspaceObj);

            } else {

                $workspaceObj->stop();
                $self->ivDelete('workspaceHash', 0);
            }
        }

        $self->ivUndef('defaultWorkspaceObj');

        # Check there are no workspaces, workspace grids, 'grid' windows, 'free' windows or
        #   textviews left (for error-detection purposes)
        $workspaceCount = $self->ivPairs('workspaceHash');
        if ($workspaceCount) {

            if ($workspaceCount == 1) {
                $msg = 'There was 1 un-closed workspace object';
            } else {
                $msg = 'There were ' . $workspaceCount . ' un-closed workspace objects';
            }

            $msg .= ' on desktop shutdown';

            $axmud::CLIENT->writeWarning($msg, $self->_objClass . '->stop');
        }

        $gridCount = $self->ivPairs('gridHash');
        if ($gridCount) {

            if ($gridCount == 1) {
                $msg = 'There was 1 un-closed workspace grid object';
            } else {
                $msg = 'There were ' . $gridCount . ' un-closed workspace grid objects';
            }

            $msg .= ' on desktop shutdown';

            $axmud::CLIENT->writeWarning($msg, $self->_objClass . '->stop');
        }

        $gridWinCount = $self->ivPairs('gridWinHash');
        if ($gridWinCount) {

            if ($gridWinCount == 1) {
                $msg = 'There was 1 un-closed \'grid\' window object';
            } else {
                $msg = 'There were ' . $gridWinCount . ' un-closed \'grid\' window objects';
            }

            $msg .= ' on desktop shutdown';

            $axmud::CLIENT->writeWarning($msg, $self->_objClass . '->stop');
        }

        $freeWinCount = $self->ivPairs('freeWinHash');
        if ($freeWinCount) {

            if ($freeWinCount == 1) {
                $msg = 'There was 1 un-closed \'free\' window object';
            } else {
                $msg = 'There were ' . $freeWinCount . ' un-closed \'free\' window objects';
            }

            $msg .= ' on desktop shutdown';

            $axmud::CLIENT->writeWarning($msg, $self->_objClass . '->stop');
        }

        $textViewCount = $self->ivPairs('textViewHash');
        if ($textViewCount) {

            if ($textViewCount == 1) {
                $msg = 'There was 1 un-closed textview object';
            } else {
                $msg = 'There were ' . $textViewCount . ' un-closed textview objects';
            }

            $msg .= ' on desktop shutdown';

            $axmud::CLIENT->writeWarning($msg, $self->_objClass . '->stop');
        }

        return 1;
    }

    sub setupWorkspaces {

        # Called by GA::Client->start
        # The earlier call to $self->start sets up the default workspace; this function sets up
        #   any further workspaces specified by GA::Client->initWorkspaceHash
        # Each workspace is handled by a workspace object (GA::Obj::Workspace). The workspace object
        #   performs certain tests on the workspace. If those tests fail, that workspace is not
        #   used, the workspace object is discarded, and no additional workspaces are used
        # If the test failed on the default workspace created by $self->start, that workspace is
        #   still used, but this function uses no additional workspaces
        # If $self->newWorkspaceFlag was set to FALSE by $self->start, this function uses no
        #   additional workspaces
        #
        # Expected arguments
        #   (none besides $self)
        #
        # Return values
        #   'undef' on improper arguments
        #   1 otherwise

        my ($self, $check) = @_;

        # Local variables
        my @useList;

        # Check for improper arguments
        if (defined $check) {

             return $axmud::CLIENT->writeImproper($self->_objClass . '->setupWorkspaces', @_);
        }

        if ($self->wmCtrlObj && $self->gridPermitFlag && $self->newWorkspaceFlag) {

            # Get a list of system workspace numbers (zero-indexed), in the order in which they
            #   should be used. The order depends on GA::Client->initWorkspaceDir; the returned list
            #   doesn't include the default workspace
            @useList = $self->detectWorkspaces();

            # GA::Client->specifies the workspaces that Axmud should use initially. If it specifies
            #   more workspaces than the 'default' one, create a new workspace object for each of
            #   them (but not if we've run out of available workspaces)
            foreach my $index (sort {$a <=> $b} ($axmud::CLIENT->ivKeys('initWorkspaceHash'))) {

                my ($workspace, $zonemap, $workspaceObj);

                # The default workspace, numbered 0, already has a GA::Obj::Workspace
                if ($index) {

                    $workspace = shift @useList;
                    $zonemap = $axmud::CLIENT->ivShow('initWorkspaceHash', $index);
                    if ($workspace) {

                        $self->useWorkspace($workspace, $zonemap);

lib/Games/Axmud/Obj/Desktop.pm  view on Meta::CPAN

                    if (! $zoneObj->owner) {

                        # All zones with this $ownerString should have an ->owner session set, so
                        #  give up with an error message
                        return $axmud::CLIENT->writeError(
                            'General error relinquishing zones from their owners',
                            $self->_objClass . '->relinquishZones',
                        );
                    }
                }
            }
        }

        # We have a list of 0, 1 or more zones that can be relinquished
        foreach my $zoneObj (@list) {

            $zoneObj->reset_owner();
        }

        return 1;
    }

    sub convertSpareMainWin {

        # Called by GA::Session->setMainWin
        # When there are no sessions, the single 'main' window is called a spare 'main' window
        # When a new session starts, the spare 'main' window must be converted into a normal
        #   'main' window (owned by a session, not by the GA::Client, and using a winmap that's
        #   not 'main_wait')
        #
        # Expected arguments
        #   $session    - The calling GA::Session
        #   $winObj     - The existing spare 'main' window object
        #
        # Optional arguments
        #   $winmap     - The winmap to use. Set only if a winmap has been marked as the default
        #                   winmap for this world; otherwise 'undef'
        #
        # Return values
        #   'undef' on improper arguments or if the spare 'main' window can't be converted
        #   1 otherwise

        my ($self, $session, $winObj, $winmap, $check) = @_;

        # Local variables
        my ($winmapObj, $successFlag);

        # Check for improper arguments
        if (! defined $session || ! defined $winObj || defined $check) {

            return $axmud::CLIENT->writeImproper($self->_objClass . '->convertSpareMainWin', @_);
        }

        # Update IVs
        $winObj->set_owner($session);
        $winObj->set_session($session);

        # Position it on a workspace grid (if grids are activated generally)
        if ($axmud::CLIENT->activateGridFlag && $self->gridPermitFlag) {

            OUTER: foreach my $workspaceObj ($axmud::CLIENT->desktopObj->listWorkspaces()) {

                foreach my $gridObj (
                    sort {$a->number <=> $b->number} ($workspaceObj->ivValues('gridHash'))
                ) {
                    if ($gridObj->repositionGridWin($winObj)) {

                        # 'main' window repositioned. Decide which winmap to use, if none was
                        #   specified by the calling function
                        if (! $winmap) {

                            # Use the default winmap for the window's new zone
                            $winmapObj = $winObj->workspaceObj->getWinmap(
                                'main',
                                undef,
                                $winObj->areaObj->zoneObj,
                            );

                            $winmap = $winmapObj->name;
                        }

                        # ->repositionGridWin is also called by
                        #   GA::Obj::WorkspaceGrid->applyZonemap, so its ->gridWinHash IV hasn't
                        #   been modified
                        $gridObj->add_gridWin($winObj);
                        # Window IVs must also be updated
                        $winObj->set_workspaceGridObj($gridObj);
                        $winObj->set_workspaceObj($gridObj->workspaceObj);

                        $self->updateWidgets($self->_objClass . '->convertSpareMainWin');

                        # Position operation complete
                        $successFlag = TRUE;
                        last OUTER;
                    }
                }
            }

            if (! $successFlag) {

                # Could not position the 'main' window on any workspace grid, for some reason
                return undef;
            }
        }

        # Reset the window's winmap
        if (! $winmap) {

            if ($axmud::CLIENT->activateGridFlag) {
                $winmap = $axmud::CLIENT->constDefaultEnabledWinmap;
            } else {
                $winmap = $axmud::CLIENT->constDefaultDisabledWinmap;
            }
        }

        $winObj->resetWinmap($winmap);

        return 1;
    }

    sub deconvertSpareMainWin {



( run in 0.590 second using v1.01-cache-2.11-cpan-f56aa216473 )