<commit_before>use std::{cell::RefCell, rc::Rc};

use dces::prelude::*;

use crate::{prelude::*, render::RenderContext2D, theming::Theme, tree::Tree, utils::*};

/// The `EventStateSystem` pops events from the event queue and delegates the events to the corresponding event handlers of the widgets and updates the states.
#[derive(Constructor)]
pub struct EventStateSystem {
    context_provider: ContextProvider,
    registry: Rc<RefCell<Registry>>,
    hovered_widgets: RefCell<Vec<Entity>>,
}

impl EventStateSystem {
    fn handles_enter_event(&self, entity: Entity) -> bool {
        if let Some(handlers) = self.context_provider.handler_map.borrow().get(&entity) {
            return handlers.iter().any(|handler| {
                handler.handles_event(&EventBox::new(
                    EnterEvent {
                        position: Point::default(),
                    },
                    EventStrategy::Direct,
                    entity,
                ))
            });
        }

        false
    }

    fn handles_enter_leave(&self, entity: Entity) -> bool {
        if let Some(handlers) = self.context_provider.handler_map.borrow().get(&entity) {
            return handlers.iter().any(|handler| {
                handler.handles_event(&EventBox::new(
                    LeaveEvent {
                        position: Point::default(),
                    },
                    EventStrategy::Direct,
                    entity,
                ))
            });
        }

        false
    }

    fn remove_hover(
        &self,
        entity: Entity,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        theme: &Theme,
    ) {
        if self.hovered_widgets.borrow().is_empty()
            || !self.hovered_widgets.borrow().contains(&entity)
        {
            return;
        }

        let pos = self
            .hovered_widgets
            .borrow()
            .iter()
            .position(|x| *x == entity);

        if let Some(pos) = pos {
            let removed = self.hovered_widgets.borrow_mut().remove(pos);

            remove_flag(
                "hover",
                &mut WidgetContainer::new(
                    removed,
                    ecm,
                    &theme,
                    Some(&self.context_provider.event_adapter),
                ),
            );
        }
    }

    // Remove all objects of a widget.
    fn remove_widget(
        &self,
        entity: Entity,
        theme: &Theme,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        render_context: &mut RenderContext2D,
    ) {
        {
            let registry = &mut self.registry.borrow_mut();

            let mut ctx = Context::new(
                (entity, ecm),
                &theme,
                &self.context_provider,
                render_context,
            );

            if let Some(state) = self.context_provider.states.borrow_mut().get_mut(&entity) {
                state.cleanup(registry, &mut ctx);
            }

            drop(ctx);
        }
        self.context_provider.states.borrow_mut().remove(&entity);

        ecm.remove_entity(entity);
        self.context_provider.layouts.borrow_mut().remove(&entity);
        self.context_provider
            .render_objects
            .borrow_mut()
            .remove(&entity);
        self.context_provider
            .handler_map
            .borrow_mut()
            .remove(&entity);
    }

    fn process_direct(
        &self,
        event: &EventBox,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
    ) -> bool {
        // skip all direct events on first run
        if self.context_provider.first_run.get() {
            return false;
        }

        let message_adapter = self.context_provider.message_adapter.clone();

        if event.strategy == EventStrategy::Direct {
            if let Some(handlers) = self
                .context_provider
                .handler_map
                .borrow()
                .get(&event.source)
            {
                handlers.iter().any(|handler| {
                    handler.handle_event(
                        &mut StatesContext::new(
                            &mut *self.context_provider.states.borrow_mut(),
                            ecm,
                            &message_adapter,
                        ),
                        &event,
                    )
                });
                return true;
            }
        }

        false
    }

    fn process_bottom_up_event(
        &self,
        mouse_position: Point,
        event: &EventBox,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
    ) -> bool {
        let mut matching_nodes = vec![];
        let mut update = false;

        let mut current_node = event.source;
        let root = ecm.entity_store().root();
        let mut disabled_parents = vec![];
        let message_adapter = self.context_provider.message_adapter.clone();

        let theme = ecm
            .component_store()
            .get::<Theme>("theme", root)
            .unwrap()
            .clone();

        // global key handling
        if let Ok(event) = event.downcast_ref::<KeyDownEvent>() {
            if let Ok(keyboard_state) = ecm
                .component_store_mut()
                .get_mut::<KeyboardState>("keyboard_state", root)
            {
                // Set this value on the keyboard state
                keyboard_state.set_key_state(event.event.key, true);
            }
        }

        if let Ok(event) = event.downcast_ref::<KeyUpEvent>() {
            if let Ok(keyboard_state) = ecm
                .component_store_mut()
                .get_mut::<KeyboardState>("keyboard_state", root)
            {
                // Set this value on the keyboard state
                keyboard_state.set_key_state(event.event.key, false);
            }
        }

        let mut unknown_event = true;
        let mut clipped_parent = vec![];

        loop {
            if !disabled_parents.is_empty() {
                if let Some(parent) = ecm.entity_store().parent[&current_node] {
                    if disabled_parents.contains(&parent) {
                        disabled_parents.push(current_node);
                    } else {
                        disabled_parents.clear();
                    }
                }
            }
            if let Ok(enabled) = ecm.component_store().get::<bool>("enabled", current_node) {
                if !enabled {
                    disabled_parents.push(current_node);
                }
            }

            if let Ok(visibility) = ecm
                .component_store()
                .get::<Visibility>("visibility", current_node)
            {
                if *visibility != Visibility::Visible {
                    disabled_parents.push(current_node);
                }
            }

            if disabled_parents.is_empty() {
                let mut has_handler = false;
                if let Some(handlers) = self
                    .context_provider
                    .handler_map
                    .borrow()
                    .get(&current_node)
                {
                    if handlers.iter().any(|handler| handler.handles_event(event)) {
                        has_handler = true;
                    }
                }
                if let Some(cp) = clipped_parent.last() {
                    if ecm.entity_store().parent[&current_node] == Some(*cp) {
                        clipped_parent.push(current_node);
                    } else {
                        clipped_parent.pop();
                    }
                }

                // scroll handling
                if event.downcast_ref::<ScrollEvent>().is_ok() {
                    if check_mouse_condition(
                        mouse_position,
                        &WidgetContainer::new(
                            current_node,
                            ecm,
                            &theme,
                            Some(&self.context_provider.event_adapter),
                        ),
                    ) && has_handler
                    {
                        matching_nodes.push(current_node);
                    }
                    unknown_event = false;
                }
                // click handling
                if let Ok(event) = event.downcast_ref::<ClickEvent>() {
                    if check_mouse_condition(
                        event.position,
                        &WidgetContainer::new(
                            current_node,
                            ecm,
                            &theme,
                            Some(&self.context_provider.event_adapter),
                        ),
                    ) {
                        let mut add = true;
                        if let Some(op) = clipped_parent.get(0) {
                            if !check_mouse_condition(
                                event.position,
                                &WidgetContainer::new(
                                    *op,
                                    ecm,
                                    &theme,
                                    Some(&self.context_provider.event_adapter),
                                ),
                            ) {
                                add = false;
                            }
                        }
                        if add && has_handler {
                            matching_nodes.push(current_node);
                        }
                    }
                    unknown_event = false;
                }
                // mouse down handling
                if let Ok(event) = event.downcast_ref::<MouseDownEvent>() {
                    if check_mouse_condition(
                        event.position,
                        &WidgetContainer::new(
                            current_node,
                            ecm,
                            &theme,
                            Some(&self.context_provider.event_adapter),
                        ),
                    ) {
                        let mut add = true;
                        if let Some(op) = clipped_parent.get(0) {
                            // todo: improve check path if exists
                            if !check_mouse_condition(
                                event.position,
                                &WidgetContainer::new(
                                    *op,
                                    ecm,
                                    &theme,
                                    Some(&self.context_provider.event_adapter),
                                ),
                            ) && has_handler
                            {
                                add = false;
                            }
                        }
                        if add {
                            matching_nodes.push(current_node);
                        }
                    }
                    unknown_event = false;
                }
                // mouse move handling
                if let Ok(event) = event.downcast_ref::<MouseMoveEvent>() {
                    if check_mouse_condition(
                        event.position,
                        &WidgetContainer::new(
                            current_node,
                            ecm,
                            &theme,
                            Some(&self.context_provider.event_adapter),
                        ),
                    ) {
                        // trigger mouse enter event if mouse cursor is first time over the current_node
                        if !self.hovered_widgets.borrow().contains(&current_node) {
                            // remove hover flag from last hovered node
                            if ecm
                                .component_store()
                                .get::<bool>("hover", current_node)
                                .is_ok()
                                && !self.hovered_widgets.borrow().is_empty()
                            {
                                let last = *self.hovered_widgets.borrow().last().unwrap();

                                remove_flag(
                                    "hover",
                                    &mut WidgetContainer::new(
                                        last,
                                        ecm,
                                        &theme,
                                        Some(&self.context_provider.event_adapter),
                                    ),
                                );

                                self.remove_hover(last, ecm, &theme);
                            }

                            // set hover flag to now hovered node
                            if ecm
                                .component_store()
                                .get::<bool>("hover", current_node)
                                .is_ok()
                            {
                                set_flag(
                                    "hover",
                                    &mut WidgetContainer::new(
                                        current_node,
                                        ecm,
                                        &theme,
                                        Some(&self.context_provider.event_adapter),
                                    ),
                                );

                                self.hovered_widgets.borrow_mut().push(current_node);
                            }

                            if self.handles_enter_event(current_node) {
                                self.context_provider.event_adapter.push_event_direct(
                                    current_node,
                                    EnterEvent {
                                        position: event.position,
                                    },
                                );
                            }
                        }

                        // todo add check to block mouse move inside of clipped areas of a widget
                        if has_handler {
                            matching_nodes.push(current_node);
                        }
                    }
                    // trigger mouse leave event when cursor leaves the current_node
                    else {
                        if self.hovered_widgets.borrow().contains(&current_node) {
                            if self.handles_enter_leave(current_node) {
                                self.context_provider.event_adapter.push_event_direct(
                                    current_node,
                                    LeaveEvent {
                                        position: event.position,
                                    },
                                );
                            }

                            self.remove_hover(current_node, ecm, &theme);
                        }
                    }
                    unknown_event = false;
                }

                if unknown_event
                    && *WidgetContainer::new(
                        current_node,
                        ecm,
                        &theme,
                        Some(&self.context_provider.event_adapter),
                    )
                    .get::<bool>("enabled")
                    && has_handler
                {
                    matching_nodes.push(current_node);
                }
                if let Ok(clip) = ecm.component_store().get::<bool>("clip", current_node) {
                    if *clip {
                        clipped_parent.clear();
                        clipped_parent.push(current_node);
                    }
                }
            }

            let mut it = ecm.entity_store().start_node(current_node).into_iter();
            it.next();

            if let Some(node) = it.next() {
                current_node = node;
            } else {
                break;
            }
        }

        let mut handled = false;

        for node in matching_nodes.iter().rev() {
            if let Some(handlers) = self.context_provider.handler_map.borrow().get(node) {
                handled = handlers.iter().any(|handler| {
                    handler.handle_event(
                        &mut StatesContext::new(
                            &mut *self.context_provider.states.borrow_mut(),
                            ecm,
                            &message_adapter,
                        ),
                        event,
                    )
                });

                update = true;
            }

            if handled {
                break;
            }
        }

        update
    }
}

impl System<Tree, StringComponentStore, RenderContext2D> for EventStateSystem {
    fn run_with_context(
        &self,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        render_context: &mut RenderContext2D,
    ) {
        let mut update = false;

        loop {
            {
                let mouse_position = self.context_provider.mouse_position.get();
                for event in self.context_provider.event_adapter.event_reader() {
                    if let Ok(event) = event.downcast_ref::<SystemEvent>() {
                        match event {
                            SystemEvent::Quit => {
                                // todo send close shell request
                                return;
                            }
                        }
                    }

                    match event.strategy {
                        EventStrategy::Direct => {
                            if event.strategy == EventStrategy::Direct {
                                update = self.process_direct(&event, ecm) || update;
                            }
                        }
                        // EventStrategy::TopDown => {
                        //     self.process_top_down_event(&event, ecm);
                        // }
                        EventStrategy::BottomUp => {
                            let should_update =
                                self.process_bottom_up_event(mouse_position, &event, ecm);
                            update = update || should_update;
                        }
                    }
                }
            }

            // handle states

            // crate::shell::CONSOLE.time("update-time:");

            let root = ecm.entity_store().root();

            let theme = ecm
                .component_store()
                .get::<Theme>("theme", root)
                .unwrap()
                .clone();

            let mut remove_widget_list: Vec<Entity> = vec![];

            let mut dirty_index = 0;

            loop {
                if dirty_index
                    >= ecm
                        .component_store()
                        .get::<Vec<Entity>>("dirty_widgets", root)
                        .unwrap()
                        .len()
                {
                    break;
                }

                let mut skip = false;

                let widget = *ecm
                    .component_store()
                    .get::<Vec<Entity>>("dirty_widgets", root)
                    .unwrap()
                    .get(dirty_index)
                    .unwrap();

                if !self.context_provider.states.borrow().contains_key(&widget) {
                    skip = true;
                }

                let mut keys = vec![];

                if !skip {
                    {
                        let registry = &mut self.registry.borrow_mut();

                        let mut ctx = Context::new(
                            (widget, ecm),
                            &theme,
                            &self.context_provider,
                            render_context,
                        );

                        if let Some(state) =
                            self.context_provider.states.borrow_mut().get_mut(&widget)
                        {
                            state.update(registry, &mut ctx);
                        }

                        keys.append(&mut ctx.new_states_keys());

                        remove_widget_list.append(ctx.remove_widget_list());
                        drop(ctx);

                        for key in keys {
                            let mut ctx = Context::new(
                                (key, ecm),
                                &theme,
                                &self.context_provider,
                                render_context,
                            );
                            if let Some(state) =
                                self.context_provider.states.borrow_mut().get_mut(&key)
                            {
                                state.init(registry, &mut ctx);
                            }

                            drop(ctx);
                        }
                    }

                    while let Some(remove_widget) = remove_widget_list.pop() {
                        let mut children = vec![];
                        get_all_children(&mut children, remove_widget, ecm.entity_store());

                        // remove children of target widget.
                        for entity in children.iter().rev() {
                            self.remove_widget(*entity, &theme, ecm, render_context);
                        }

                        // remove target widget
                        self.remove_widget(remove_widget, &theme, ecm, render_context);
                    }
                }

                dirty_index += 1;
            }

            // crate::shell::CONSOLE.time_end("update-time:");

            if self.context_provider.event_adapter.is_empty() {
                break;
            }
        }
    }
}
<commit_msg>clippy<commit_after>use std::{cell::RefCell, rc::Rc};

use dces::prelude::*;

use crate::{prelude::*, render::RenderContext2D, theming::Theme, tree::Tree, utils::*};

/// The `EventStateSystem` pops events from the event queue and delegates the events to the corresponding event handlers of the widgets and updates the states.
#[derive(Constructor)]
pub struct EventStateSystem {
    context_provider: ContextProvider,
    registry: Rc<RefCell<Registry>>,
    hovered_widgets: RefCell<Vec<Entity>>,
}

impl EventStateSystem {
    fn handles_enter_event(&self, entity: Entity) -> bool {
        if let Some(handlers) = self.context_provider.handler_map.borrow().get(&entity) {
            return handlers.iter().any(|handler| {
                handler.handles_event(&EventBox::new(
                    EnterEvent {
                        position: Point::default(),
                    },
                    EventStrategy::Direct,
                    entity,
                ))
            });
        }

        false
    }

    fn handles_enter_leave(&self, entity: Entity) -> bool {
        if let Some(handlers) = self.context_provider.handler_map.borrow().get(&entity) {
            return handlers.iter().any(|handler| {
                handler.handles_event(&EventBox::new(
                    LeaveEvent {
                        position: Point::default(),
                    },
                    EventStrategy::Direct,
                    entity,
                ))
            });
        }

        false
    }

    fn remove_hover(
        &self,
        entity: Entity,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        theme: &Theme,
    ) {
        if self.hovered_widgets.borrow().is_empty()
            || !self.hovered_widgets.borrow().contains(&entity)
        {
            return;
        }

        let pos = self
            .hovered_widgets
            .borrow()
            .iter()
            .position(|x| *x == entity);

        if let Some(pos) = pos {
            let removed = self.hovered_widgets.borrow_mut().remove(pos);

            remove_flag(
                "hover",
                &mut WidgetContainer::new(
                    removed,
                    ecm,
                    &theme,
                    Some(&self.context_provider.event_adapter),
                ),
            );
        }
    }

    // Remove all objects of a widget.
    fn remove_widget(
        &self,
        entity: Entity,
        theme: &Theme,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        render_context: &mut RenderContext2D,
    ) {
        {
            let registry = &mut self.registry.borrow_mut();

            let mut ctx = Context::new(
                (entity, ecm),
                &theme,
                &self.context_provider,
                render_context,
            );

            if let Some(state) = self.context_provider.states.borrow_mut().get_mut(&entity) {
                state.cleanup(registry, &mut ctx);
            }

            drop(ctx);
        }
        self.context_provider.states.borrow_mut().remove(&entity);

        ecm.remove_entity(entity);
        self.context_provider.layouts.borrow_mut().remove(&entity);
        self.context_provider
            .render_objects
            .borrow_mut()
            .remove(&entity);
        self.context_provider
            .handler_map
            .borrow_mut()
            .remove(&entity);
    }

    fn process_direct(
        &self,
        event: &EventBox,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
    ) -> bool {
        // skip all direct events on first run
        if self.context_provider.first_run.get() {
            return false;
        }

        let message_adapter = self.context_provider.message_adapter.clone();

        if event.strategy == EventStrategy::Direct {
            if let Some(handlers) = self
                .context_provider
                .handler_map
                .borrow()
                .get(&event.source)
            {
                handlers.iter().any(|handler| {
                    handler.handle_event(
                        &mut StatesContext::new(
                            &mut *self.context_provider.states.borrow_mut(),
                            ecm,
                            &message_adapter,
                        ),
                        &event,
                    )
                });
                return true;
            }
        }

        false
    }

    fn process_bottom_up_event(
        &self,
        mouse_position: Point,
        event: &EventBox,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
    ) -> bool {
        let mut matching_nodes = vec![];
        let mut update = false;

        let mut current_node = event.source;
        let root = ecm.entity_store().root();
        let mut disabled_parents = vec![];
        let message_adapter = self.context_provider.message_adapter.clone();

        let theme = ecm
            .component_store()
            .get::<Theme>("theme", root)
            .unwrap()
            .clone();

        // global key handling
        if let Ok(event) = event.downcast_ref::<KeyDownEvent>() {
            if let Ok(keyboard_state) = ecm
                .component_store_mut()
                .get_mut::<KeyboardState>("keyboard_state", root)
            {
                // Set this value on the keyboard state
                keyboard_state.set_key_state(event.event.key, true);
            }
        }

        if let Ok(event) = event.downcast_ref::<KeyUpEvent>() {
            if let Ok(keyboard_state) = ecm
                .component_store_mut()
                .get_mut::<KeyboardState>("keyboard_state", root)
            {
                // Set this value on the keyboard state
                keyboard_state.set_key_state(event.event.key, false);
            }
        }

        let mut unknown_event = true;
        let mut clipped_parent = vec![];

        loop {
            if !disabled_parents.is_empty() {
                if let Some(parent) = ecm.entity_store().parent[&current_node] {
                    if disabled_parents.contains(&parent) {
                        disabled_parents.push(current_node);
                    } else {
                        disabled_parents.clear();
                    }
                }
            }
            if let Ok(enabled) = ecm.component_store().get::<bool>("enabled", current_node) {
                if !enabled {
                    disabled_parents.push(current_node);
                }
            }

            if let Ok(visibility) = ecm
                .component_store()
                .get::<Visibility>("visibility", current_node)
            {
                if *visibility != Visibility::Visible {
                    disabled_parents.push(current_node);
                }
            }

            if disabled_parents.is_empty() {
                let mut has_handler = false;
                if let Some(handlers) = self
                    .context_provider
                    .handler_map
                    .borrow()
                    .get(&current_node)
                {
                    if handlers.iter().any(|handler| handler.handles_event(event)) {
                        has_handler = true;
                    }
                }
                if let Some(cp) = clipped_parent.last() {
                    if ecm.entity_store().parent[&current_node] == Some(*cp) {
                        clipped_parent.push(current_node);
                    } else {
                        clipped_parent.pop();
                    }
                }

                // scroll handling
                if event.downcast_ref::<ScrollEvent>().is_ok() {
                    if check_mouse_condition(
                        mouse_position,
                        &WidgetContainer::new(
                            current_node,
                            ecm,
                            &theme,
                            Some(&self.context_provider.event_adapter),
                        ),
                    ) && has_handler
                    {
                        matching_nodes.push(current_node);
                    }
                    unknown_event = false;
                }
                // click handling
                if let Ok(event) = event.downcast_ref::<ClickEvent>() {
                    if check_mouse_condition(
                        event.position,
                        &WidgetContainer::new(
                            current_node,
                            ecm,
                            &theme,
                            Some(&self.context_provider.event_adapter),
                        ),
                    ) {
                        let mut add = true;
                        if let Some(op) = clipped_parent.get(0) {
                            if !check_mouse_condition(
                                event.position,
                                &WidgetContainer::new(
                                    *op,
                                    ecm,
                                    &theme,
                                    Some(&self.context_provider.event_adapter),
                                ),
                            ) {
                                add = false;
                            }
                        }
                        if add && has_handler {
                            matching_nodes.push(current_node);
                        }
                    }
                    unknown_event = false;
                }
                // mouse down handling
                if let Ok(event) = event.downcast_ref::<MouseDownEvent>() {
                    if check_mouse_condition(
                        event.position,
                        &WidgetContainer::new(
                            current_node,
                            ecm,
                            &theme,
                            Some(&self.context_provider.event_adapter),
                        ),
                    ) {
                        let mut add = true;
                        if let Some(op) = clipped_parent.get(0) {
                            // todo: improve check path if exists
                            if !check_mouse_condition(
                                event.position,
                                &WidgetContainer::new(
                                    *op,
                                    ecm,
                                    &theme,
                                    Some(&self.context_provider.event_adapter),
                                ),
                            ) && has_handler
                            {
                                add = false;
                            }
                        }
                        if add {
                            matching_nodes.push(current_node);
                        }
                    }
                    unknown_event = false;
                }
                // mouse move handling
                if let Ok(event) = event.downcast_ref::<MouseMoveEvent>() {
                    if check_mouse_condition(
                        event.position,
                        &WidgetContainer::new(
                            current_node,
                            ecm,
                            &theme,
                            Some(&self.context_provider.event_adapter),
                        ),
                    ) {
                        // trigger mouse enter event if mouse cursor is first time over the current_node
                        if !self.hovered_widgets.borrow().contains(&current_node) {
                            // remove hover flag from last hovered node
                            if ecm
                                .component_store()
                                .get::<bool>("hover", current_node)
                                .is_ok()
                                && !self.hovered_widgets.borrow().is_empty()
                            {
                                let last = *self.hovered_widgets.borrow().last().unwrap();

                                remove_flag(
                                    "hover",
                                    &mut WidgetContainer::new(
                                        last,
                                        ecm,
                                        &theme,
                                        Some(&self.context_provider.event_adapter),
                                    ),
                                );

                                self.remove_hover(last, ecm, &theme);
                            }

                            // set hover flag to now hovered node
                            if ecm
                                .component_store()
                                .get::<bool>("hover", current_node)
                                .is_ok()
                            {
                                set_flag(
                                    "hover",
                                    &mut WidgetContainer::new(
                                        current_node,
                                        ecm,
                                        &theme,
                                        Some(&self.context_provider.event_adapter),
                                    ),
                                );

                                self.hovered_widgets.borrow_mut().push(current_node);
                            }

                            if self.handles_enter_event(current_node) {
                                self.context_provider.event_adapter.push_event_direct(
                                    current_node,
                                    EnterEvent {
                                        position: event.position,
                                    },
                                );
                            }
                        }

                        // todo add check to block mouse move inside of clipped areas of a widget
                        if has_handler {
                            matching_nodes.push(current_node);
                        }
                    }
                    // trigger mouse leave event when cursor leaves the current_node
                    else if self.hovered_widgets.borrow().contains(&current_node) {
                        if self.handles_enter_leave(current_node) {
                            self.context_provider.event_adapter.push_event_direct(
                                current_node,
                                LeaveEvent {
                                    position: event.position,
                                },
                            );
                        }

                        self.remove_hover(current_node, ecm, &theme);
                    }

                    unknown_event = false;
                }

                if unknown_event
                    && *WidgetContainer::new(
                        current_node,
                        ecm,
                        &theme,
                        Some(&self.context_provider.event_adapter),
                    )
                    .get::<bool>("enabled")
                    && has_handler
                {
                    matching_nodes.push(current_node);
                }
                if let Ok(clip) = ecm.component_store().get::<bool>("clip", current_node) {
                    if *clip {
                        clipped_parent.clear();
                        clipped_parent.push(current_node);
                    }
                }
            }

            let mut it = ecm.entity_store().start_node(current_node).into_iter();
            it.next();

            if let Some(node) = it.next() {
                current_node = node;
            } else {
                break;
            }
        }

        let mut handled = false;

        for node in matching_nodes.iter().rev() {
            if let Some(handlers) = self.context_provider.handler_map.borrow().get(node) {
                handled = handlers.iter().any(|handler| {
                    handler.handle_event(
                        &mut StatesContext::new(
                            &mut *self.context_provider.states.borrow_mut(),
                            ecm,
                            &message_adapter,
                        ),
                        event,
                    )
                });

                update = true;
            }

            if handled {
                break;
            }
        }

        update
    }
}

impl System<Tree, StringComponentStore, RenderContext2D> for EventStateSystem {
    fn run_with_context(
        &self,
        ecm: &mut EntityComponentManager<Tree, StringComponentStore>,
        render_context: &mut RenderContext2D,
    ) {
        let mut update = false;

        loop {
            {
                let mouse_position = self.context_provider.mouse_position.get();
                for event in self.context_provider.event_adapter.event_reader() {
                    if let Ok(event) = event.downcast_ref::<SystemEvent>() {
                        match event {
                            SystemEvent::Quit => {
                                // todo send close shell request
                                return;
                            }
                        }
                    }

                    match event.strategy {
                        EventStrategy::Direct => {
                            if event.strategy == EventStrategy::Direct {
                                update = self.process_direct(&event, ecm) || update;
                            }
                        }
                        // EventStrategy::TopDown => {
                        //     self.process_top_down_event(&event, ecm);
                        // }
                        EventStrategy::BottomUp => {
                            let should_update =
                                self.process_bottom_up_event(mouse_position, &event, ecm);
                            update = update || should_update;
                        }
                    }
                }
            }

            // handle states

            // crate::shell::CONSOLE.time("update-time:");

            let root = ecm.entity_store().root();

            let theme = ecm
                .component_store()
                .get::<Theme>("theme", root)
                .unwrap()
                .clone();

            let mut remove_widget_list: Vec<Entity> = vec![];

            let mut dirty_index = 0;

            loop {
                if dirty_index
                    >= ecm
                        .component_store()
                        .get::<Vec<Entity>>("dirty_widgets", root)
                        .unwrap()
                        .len()
                {
                    break;
                }

                let mut skip = false;

                let widget = *ecm
                    .component_store()
                    .get::<Vec<Entity>>("dirty_widgets", root)
                    .unwrap()
                    .get(dirty_index)
                    .unwrap();

                if !self.context_provider.states.borrow().contains_key(&widget) {
                    skip = true;
                }

                let mut keys = vec![];

                if !skip {
                    {
                        let registry = &mut self.registry.borrow_mut();

                        let mut ctx = Context::new(
                            (widget, ecm),
                            &theme,
                            &self.context_provider,
                            render_context,
                        );

                        if let Some(state) =
                            self.context_provider.states.borrow_mut().get_mut(&widget)
                        {
                            state.update(registry, &mut ctx);
                        }

                        keys.append(&mut ctx.new_states_keys());

                        remove_widget_list.append(ctx.remove_widget_list());
                        drop(ctx);

                        for key in keys {
                            let mut ctx = Context::new(
                                (key, ecm),
                                &theme,
                                &self.context_provider,
                                render_context,
                            );
                            if let Some(state) =
                                self.context_provider.states.borrow_mut().get_mut(&key)
                            {
                                state.init(registry, &mut ctx);
                            }

                            drop(ctx);
                        }
                    }

                    while let Some(remove_widget) = remove_widget_list.pop() {
                        let mut children = vec![];
                        get_all_children(&mut children, remove_widget, ecm.entity_store());

                        // remove children of target widget.
                        for entity in children.iter().rev() {
                            self.remove_widget(*entity, &theme, ecm, render_context);
                        }

                        // remove target widget
                        self.remove_widget(remove_widget, &theme, ecm, render_context);
                    }
                }

                dirty_index += 1;
            }

            // crate::shell::CONSOLE.time_end("update-time:");

            if self.context_provider.event_adapter.is_empty() {
                break;
            }
        }
    }
}
<|endoftext|>


<commit_before>//! This module implements the logic needed to apply the proper analysis passes to simplify the IR.

use petgraph::algo::tarjan_scc;
use petgraph::Graph;

use std::any::Any;
use std::collections::HashMap;
use std::fmt::Debug;

use analysis::analyzer;
use analysis::analyzer::{
    Action, AnalyzerInfo, AnalyzerKind, Change, FuncAnalyzer, ModuleAnalyzer,
};
use analysis::arithmetic::{ArithChange, Arithmetic};
use analysis::copy_propagation::CopyPropagation;
use analysis::cse::cse::CSE;
use analysis::cse::ssasort::Sorter;
use analysis::dce::DCE;
use analysis::functions::fix_ssa_opcalls::CallSiteFixer;
use analysis::functions::infer_regusage::Inferer;
use analysis::inst_combine::Combiner;
use analysis::interproc::fixcall::CallFixer;
use analysis::sccp::SCCP;
use frontend::radeco_containers::{RadecoFunction, RadecoModule};
use middle::regfile::SubRegisterFile;

fn sort_by_requires(analyzers: &Vec<AnalyzerKind>) -> impl Iterator<Item = AnalyzerKind> {
    // Build the dependency graph.
    let mut graph = Graph::<AnalyzerKind, ()>::new();
    let mut kind2id = HashMap::new();

    for analyzer in analyzers {
        let n = kind2id
            .entry(*analyzer)
            .or_insert_with(|| graph.add_node(*analyzer))
            .clone();
        let info: &'static AnalyzerInfo = From::from(*analyzer);

        for dep in info.requires {
            let d = kind2id
                .entry(*dep)
                .or_insert_with(|| graph.add_node(*dep))
                .clone();
            graph.add_edge(d, n, ());
        }
    }

    // Compute the reverse topological sorting using the Tarjan's algorithm. If SCCs are present
    // we can safely run the analyzers within the same SCC in arbitrary order because the `requires`
    // property is not garanteed to be respected.
    let sccs = tarjan_scc(&graph);

    sccs.into_iter().flatten().map(move |id| graph[id]).rev()
}

pub trait EngineResult: Any + Debug {}

pub trait Engine: Any + Debug {
    fn run_module(
        &self,
        rmod: &mut RadecoModule,
        regfile: &SubRegisterFile,
    ) -> Option<Box<EngineResult>>;
    fn run_func(&self, rfn: &mut RadecoFunction) -> Option<Box<EngineResult>>;
}

/// Radeco's default engine.
#[derive(Debug)]
pub struct RadecoEngine {
    max_iteration: u32,
}

impl RadecoEngine {
    pub fn new(max_iteration: u32) -> Self {
        RadecoEngine {
            max_iteration: max_iteration,
        }
    }
}

impl Engine for RadecoEngine {
    fn run_module(
        &self,
        rmod: &mut RadecoModule,
        regfile: &SubRegisterFile,
    ) -> Option<Box<EngineResult>> {
        // Analyze preserved for all functions.
        {
            let bp_name = regfile.get_name_by_alias(&"BP".to_string());
            let bp_name = bp_name.map(|s| s.to_owned());
            let sp_name = regfile.get_name_by_alias(&"SP".to_string());
            let sp_name = sp_name.map(|s| s.to_owned());
            let mut callfixer = CallFixer::new(rmod, bp_name, sp_name);
            callfixer.rounded_analysis();
        }

        // Fix call sites
        let mut call_site_fixer = CallSiteFixer::new();
        call_site_fixer.analyze(rmod, None::<fn(_) -> _>);

        // Infer calling conventions
        let mut inferer = Inferer::new((*regfile).clone());
        inferer.analyze(rmod, None::<fn(_) -> _>);

        for rfn in rmod.functions.values_mut() {
            self.run_func(rfn);
        }

        None
    }

    fn run_func(&self, rfn: &mut RadecoFunction) -> Option<Box<EngineResult>> {
        // Try to convert the condition codes to relational operators. This should be done before
        // all the other passes.
        let mut arithmetic = Arithmetic::new();
        arithmetic.analyze(
            rfn,
            Some(|change: Box<Change>| {
                let change = change.as_any().downcast_ref::<ArithChange>().unwrap();
                if change.new_expr.contains("OpEq")
                    || change.new_expr.contains("OpGt")
                    || change.new_expr.contains("OpLt")
                {
                    Action::Apply
                } else {
                    Action::Skip
                }
            }),
        );

        {
            // Sort the IR.
            let mut sorter = Sorter::new(rfn.ssa_mut());
            sorter.run();
        }

        let mut analyzers = sort_by_requires(&analyzer::all_func_analyzers());

        // Run iteratively all the available analyzers until a stable point or the maximum
        // number of iterations is reached.
        for _ in 0..self.max_iteration {
            let mut stable = true;

            // Build and run the analyzers.
            while let Some(analyzer) = analyzers.next() {
                // If the policy is called then there is still something to change, thus this is
                // not a stable point.
                let policy = |_| {
                    stable = false;
                    Action::Apply
                };

                match analyzer {
                    AnalyzerKind::Arithmetic => {
                        let mut arithmetic = Arithmetic::new();
                        arithmetic.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::Combiner => {
                        let mut combiner = Combiner::new();
                        combiner.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::CopyPropagation => {
                        let mut copy_propagation = CopyPropagation::new();
                        copy_propagation.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::CSE => {
                        let mut cse = CSE::new();
                        cse.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::DCE => {
                        let mut dce = DCE::new();
                        dce.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::SCCP => {
                        let mut sccp = SCCP::new();
                        sccp.analyze(rfn, Some(policy));
                    }
                    _ => (),
                }
            }

            if stable {
                break;
            }
        }

        None
    }
}
<commit_msg>Run func analysis in parallel<commit_after>//! This module implements the logic needed to apply the proper analysis passes to simplify the IR.

use petgraph::algo::tarjan_scc;
use petgraph::Graph;

use rayon::prelude::*;

use std::any::Any;
use std::collections::HashMap;
use std::fmt::Debug;

use analysis::analyzer;
use analysis::analyzer::{
    Action, AnalyzerInfo, AnalyzerKind, Change, FuncAnalyzer, ModuleAnalyzer,
};
use analysis::arithmetic::{ArithChange, Arithmetic};
use analysis::copy_propagation::CopyPropagation;
use analysis::cse::cse::CSE;
use analysis::cse::ssasort::Sorter;
use analysis::dce::DCE;
use analysis::functions::fix_ssa_opcalls::CallSiteFixer;
use analysis::functions::infer_regusage::Inferer;
use analysis::inst_combine::Combiner;
use analysis::interproc::fixcall::CallFixer;
use analysis::sccp::SCCP;
use frontend::radeco_containers::{RadecoFunction, RadecoModule};
use middle::regfile::SubRegisterFile;

fn sort_by_requires(analyzers: &Vec<AnalyzerKind>) -> impl Iterator<Item = AnalyzerKind> {
    // Build the dependency graph.
    let mut graph = Graph::<AnalyzerKind, ()>::new();
    let mut kind2id = HashMap::new();

    for analyzer in analyzers {
        let n = kind2id
            .entry(*analyzer)
            .or_insert_with(|| graph.add_node(*analyzer))
            .clone();
        let info: &'static AnalyzerInfo = From::from(*analyzer);

        for dep in info.requires {
            let d = kind2id
                .entry(*dep)
                .or_insert_with(|| graph.add_node(*dep))
                .clone();
            graph.add_edge(d, n, ());
        }
    }

    // Compute the reverse topological sorting using the Tarjan's algorithm. If SCCs are present
    // we can safely run the analyzers within the same SCC in arbitrary order because the `requires`
    // property is not garanteed to be respected.
    let sccs = tarjan_scc(&graph);

    sccs.into_iter().flatten().map(move |id| graph[id]).rev()
}

pub trait EngineResult: Any + Debug {}

pub trait Engine: Any + Debug {
    fn run_module(
        &self,
        rmod: &mut RadecoModule,
        regfile: &SubRegisterFile,
    ) -> Option<Box<EngineResult>>;
    fn run_func(&self, rfn: &mut RadecoFunction) -> Option<Box<EngineResult>>;
}

/// Radeco's default engine.
#[derive(Debug)]
pub struct RadecoEngine {
    max_iteration: u32,
}

impl RadecoEngine {
    pub fn new(max_iteration: u32) -> Self {
        RadecoEngine {
            max_iteration: max_iteration,
        }
    }
}

impl Engine for RadecoEngine {
    fn run_module(
        &self,
        rmod: &mut RadecoModule,
        regfile: &SubRegisterFile,
    ) -> Option<Box<EngineResult>> {
        // Analyze preserved for all functions.
        {
            let bp_name = regfile.get_name_by_alias(&"BP".to_string());
            let bp_name = bp_name.map(|s| s.to_owned());
            let sp_name = regfile.get_name_by_alias(&"SP".to_string());
            let sp_name = sp_name.map(|s| s.to_owned());
            let mut callfixer = CallFixer::new(rmod, bp_name, sp_name);
            callfixer.rounded_analysis();
        }

        // Fix call sites
        let mut call_site_fixer = CallSiteFixer::new();
        call_site_fixer.analyze(rmod, None::<fn(_) -> _>);

        // Infer calling conventions
        let mut inferer = Inferer::new((*regfile).clone());
        inferer.analyze(rmod, None::<fn(_) -> _>);

        rmod.functions.par_iter_mut().for_each(|(_, rfn)| {
            self.run_func(rfn);
        });

        None
    }

    fn run_func(&self, rfn: &mut RadecoFunction) -> Option<Box<EngineResult>> {
        // Try to convert the condition codes to relational operators. This should be done before
        // all the other passes.
        let mut arithmetic = Arithmetic::new();
        arithmetic.analyze(
            rfn,
            Some(|change: Box<Change>| {
                let change = change.as_any().downcast_ref::<ArithChange>().unwrap();
                if change.new_expr.contains("OpEq")
                    || change.new_expr.contains("OpGt")
                    || change.new_expr.contains("OpLt")
                {
                    Action::Apply
                } else {
                    Action::Skip
                }
            }),
        );

        {
            // Sort the IR.
            let mut sorter = Sorter::new(rfn.ssa_mut());
            sorter.run();
        }

        let mut analyzers = sort_by_requires(&analyzer::all_func_analyzers());

        // Run iteratively all the available analyzers until a stable point or the maximum
        // number of iterations is reached.
        for _ in 0..self.max_iteration {
            let mut stable = true;

            // Build and run the analyzers.
            while let Some(analyzer) = analyzers.next() {
                // If the policy is called then there is still something to change, thus this is
                // not a stable point.
                let policy = |_| {
                    stable = false;
                    Action::Apply
                };

                match analyzer {
                    AnalyzerKind::Arithmetic => {
                        let mut arithmetic = Arithmetic::new();
                        arithmetic.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::Combiner => {
                        let mut combiner = Combiner::new();
                        combiner.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::CopyPropagation => {
                        let mut copy_propagation = CopyPropagation::new();
                        copy_propagation.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::CSE => {
                        let mut cse = CSE::new();
                        cse.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::DCE => {
                        let mut dce = DCE::new();
                        dce.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::SCCP => {
                        let mut sccp = SCCP::new();
                        sccp.analyze(rfn, Some(policy));
                    }
                    _ => (),
                }
            }

            if stable {
                break;
            }
        }

        None
    }
}
<|endoftext|>


<commit_before>use config::gobjects::GObject;
use env::Env;
use library;
use nameutil::*;
use super::*;
use super::imports::Imports;
use traits::*;
use version::Version;

#[derive(Default)]
pub struct Info {
    pub full_name: String,
    pub class_tid: library::TypeId,
    pub name: String,
    pub c_type: String,
    pub get_type: String,
    pub parents: Vec<general::StatusedTypeId>,
    pub implements: Vec<general::StatusedTypeId>,
    pub has_children: bool,
    pub functions: Vec<functions::Info>,
    pub has_constructors: bool,
    pub has_methods: bool,
    pub has_functions: bool,
    pub imports: Imports,
    pub version: Option<Version>,
}

impl Info {
    ///TODO: return iterator
    pub fn constructors(&self) -> Vec<&functions::Info> {
        self.functions.iter()
            .filter(|f| f.kind == library::FunctionKind::Constructor)
            .collect()
    }

    pub fn methods(&self) -> Vec<&functions::Info> {
        self.functions.iter()
            .filter(|f| f.kind == library::FunctionKind::Method)
            .collect()
    }

    pub fn functions(&self) -> Vec<&functions::Info> {
        self.functions.iter()
            .filter(|f| f.kind == library::FunctionKind::Function)
            .collect()
    }
}

pub fn new(env: &Env, obj: &GObject) -> Option<Info> {
    let full_name = obj.name.clone();

    let class_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(class_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let klass: &library::Class = match type_.maybe_ref() {
        Some(klass) => klass,
        None => return None,
    };

    let mut imports = Imports::new();
    imports.add("glib::translate::*".into(), None);
    imports.add("ffi".into(), None);

    let parents = parents::analyze_class(env, klass, &mut imports);
    let implements = implements::analyze(env, klass, &mut imports);

    let mut has_children = false;

    for child_tid in &klass.children {
        let child_name = child_tid.full_name(&env.library);
        let status = env.config.objects.get(&child_name)
            .map(|o| o.status)
            .unwrap_or(Default::default());
        if status.normal() {
            has_children = true;
            break;
        }
    }

    if has_children {
        imports.add("glib::object::Upcast".into(), None);
    }

    let functions =
        functions::analyze(env, &klass.functions, class_tid, &obj, &mut imports);

    let version = functions.iter().filter_map(|f| f.version).min();

    //don't `use` yourself
    imports.remove(&name);

    let mut info = Info {
        full_name: full_name,
        class_tid: class_tid,
        name: name,
        c_type: klass.c_type.clone(),
        get_type: klass.glib_get_type.clone(),
        parents: parents,
        implements: implements,
        has_children: has_children,
        functions: functions,
        imports: imports,
        version: version,
        .. Default::default()
    };

    let has_constructors = !info.constructors().is_empty();
    let has_methods = !info.methods().is_empty();
    let has_functions = !info.functions().is_empty();

    info.has_constructors = has_constructors;
    info.has_methods = has_methods;
    info.has_functions = has_functions;
    Some(info)
}

pub fn interface(env: &Env, obj: &GObject) -> Option<Info> {
    let full_name = obj.name.clone();

    let iface_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(iface_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let iface: &library::Interface = match type_.maybe_ref() {
        Some(iface) => iface,
        None => return None,
    };

    let mut imports = Imports::new();
    imports.add("glib::translate::*".into(), None);
    imports.add("ffi".into(), None);
    imports.add("glib::object::Upcast".into(), None);

    let parents = parents::analyze_interface(env, iface, &mut imports);

    let functions =
        functions::analyze(env, &iface.functions, iface_tid, &obj, &mut imports);

    let version = functions.iter().filter_map(|f| f.version).min();

    //don't `use` yourself
    imports.remove(&name);

    let mut info = Info {
        full_name: full_name,
        class_tid: iface_tid,
        name: name,
        c_type: iface.c_type.clone(),
        get_type: iface.glib_get_type.clone(),
        parents: parents,
        has_children: true,
        functions: functions,
        imports: imports,
        version: version,
        .. Default::default()
    };

    info.has_methods = !info.methods().is_empty();
    Some(info)
}
<commit_msg>Add special functions handling to objects analysis<commit_after>use config::gobjects::GObject;
use env::Env;
use library;
use nameutil::*;
use super::*;
use super::imports::Imports;
use traits::*;
use version::Version;

#[derive(Default)]
pub struct Info {
    pub full_name: String,
    pub class_tid: library::TypeId,
    pub name: String,
    pub c_type: String,
    pub get_type: String,
    pub parents: Vec<general::StatusedTypeId>,
    pub implements: Vec<general::StatusedTypeId>,
    pub has_children: bool,
    pub functions: Vec<functions::Info>,
    pub specials: special_functions::Infos,
    pub has_constructors: bool,
    pub has_methods: bool,
    pub has_functions: bool,
    pub imports: Imports,
    pub version: Option<Version>,
}

impl Info {
    ///TODO: return iterator
    pub fn constructors(&self) -> Vec<&functions::Info> {
        self.functions.iter()
            .filter(|f| f.kind == library::FunctionKind::Constructor)
            .collect()
    }

    pub fn methods(&self) -> Vec<&functions::Info> {
        self.functions.iter()
            .filter(|f| f.kind == library::FunctionKind::Method)
            .collect()
    }

    pub fn functions(&self) -> Vec<&functions::Info> {
        self.functions.iter()
            .filter(|f| f.kind == library::FunctionKind::Function)
            .collect()
    }
}

pub fn new(env: &Env, obj: &GObject) -> Option<Info> {
    let full_name = obj.name.clone();

    let class_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(class_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let klass: &library::Class = match type_.maybe_ref() {
        Some(klass) => klass,
        None => return None,
    };

    let mut imports = Imports::new();
    imports.add("glib::translate::*".into(), None);
    imports.add("ffi".into(), None);

    let parents = parents::analyze_class(env, klass, &mut imports);
    let implements = implements::analyze(env, klass, &mut imports);

    let mut has_children = false;

    for child_tid in &klass.children {
        let child_name = child_tid.full_name(&env.library);
        let status = env.config.objects.get(&child_name)
            .map(|o| o.status)
            .unwrap_or(Default::default());
        if status.normal() {
            has_children = true;
            break;
        }
    }

    if has_children {
        imports.add("glib::object::Upcast".into(), None);
    }

    let functions =
        functions::analyze(env, &klass.functions, class_tid, &obj, &mut imports);
    let specials = special_functions::extract(&mut functions);

    let version = functions.iter().filter_map(|f| f.version).min();

    //don't `use` yourself
    imports.remove(&name);

    let mut info = Info {
        full_name: full_name,
        class_tid: class_tid,
        name: name,
        c_type: klass.c_type.clone(),
        get_type: klass.glib_get_type.clone(),
        parents: parents,
        implements: implements,
        has_children: has_children,
        functions: functions,
        specials: specials,
        imports: imports,
        version: version,
        .. Default::default()
    };

    let has_constructors = !info.constructors().is_empty();
    let has_methods = !info.methods().is_empty();
    let has_functions = !info.functions().is_empty();

    info.has_constructors = has_constructors;
    info.has_methods = has_methods;
    info.has_functions = has_functions;
    Some(info)
}

pub fn interface(env: &Env, obj: &GObject) -> Option<Info> {
    let full_name = obj.name.clone();

    let iface_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(iface_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let iface: &library::Interface = match type_.maybe_ref() {
        Some(iface) => iface,
        None => return None,
    };

    let mut imports = Imports::new();
    imports.add("glib::translate::*".into(), None);
    imports.add("ffi".into(), None);
    imports.add("glib::object::Upcast".into(), None);

    let parents = parents::analyze_interface(env, iface, &mut imports);

    let functions =
        functions::analyze(env, &iface.functions, iface_tid, &obj, &mut imports);

    let version = functions.iter().filter_map(|f| f.version).min();

    //don't `use` yourself
    imports.remove(&name);

    let mut info = Info {
        full_name: full_name,
        class_tid: iface_tid,
        name: name,
        c_type: iface.c_type.clone(),
        get_type: iface.glib_get_type.clone(),
        parents: parents,
        has_children: true,
        functions: functions,
        imports: imports,
        version: version,
        .. Default::default()
    };

    info.has_methods = !info.methods().is_empty();
    Some(info)
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! `x86_64` architecture-specific implementation.
// pub mod cpu;
pub mod drivers;
pub mod interrupts;

#[path = "../x86_all/bda.rs"] pub mod bda;
#[path = "../x86_all/multiboot2.rs"] pub mod multiboot2;

pub const ARCH_BITS: u8 = 64;

extern {
    // TODO: It would be really nice if there was a less ugly way of doing
    // this... (read: after the Revolution when we add memory regions to the
    // heap programmatically.)
    #[link_name = "heap_base_addr"]
    #[linkage = "external"]
    pub static HEAP_BASE: *mut u8;
    #[link_name = "heap_top_addr"]
    #[linkage = "external"]
    pub static HEAP_TOP: *mut u8;
    // Of course, we will still need to export the kernel stack addresses like
    // this, but it would be nice if they could be, i dont know, not mut u8s
    // pointers, like God intended.
    #[link_name = "stack_base"]
    pub static STACK_BASE: *mut u8;
    #[link_name = "stack_top"]
    pub static STACK_TOP: *mut u8;
}

use memory::PAddr;

/// Trampoline to ensure we have a correct stack frame for calling [`arch_init`]
///
/// I have no idea why this works, but it does.
///
/// [`arch_init`]: fn.arch_init
#[naked]
#[no_mangle]
pub unsafe extern "C" fn long_mode_init() {
    asm!("movabsq $$(stack_top), %rsp");
    asm!("mov ax, 0
          mov ss, ax
          mov ds, ax
          mov es, ax
          mov fs, ax
          mov gs, ax
          call arch_init"
        :::: "intel");

}

/// Entry point for architecture-specific kernel init
///
/// This expects to be passed the address of a valid
/// Multiboot 2 info struct. It's the bootloader's responsibility to ensure
/// that this is passed in the correct register as expected by the calling
/// convention (`edi` on x86). If this isn't there, you can expect to have a
/// bad problem and not go to space today.
#[no_mangle]
pub extern "C" fn arch_init(multiboot_addr: PAddr) {
    use cpu::{control_regs, msr};
    use elf;
    use params::InitParams;

    kinfoln!(dots: " . ", "Beginning `arch_init()` for x86_64");

    ::io::term::CONSOLE.lock().clear();
    ::logger::initialize()
        .expect("Could not initialize logger!");

    // -- Unpack multiboot tag ------------------------------------------------
    kinfoln!( dots: " . "
            , "trying to unpack multiboot info at {:#p}"
            , multiboot_addr);

    // try to interpret the structure at the multiboot address as a multiboot
    // info struct. if it's invalid, fail.
    let boot_info
        = unsafe { multiboot2::Info::from(multiboot_addr)
                    .expect("Could not unpack multiboot2 information!") };

    // Extract ELF sections tag from the multiboot info
    let elf_sections_tag
        = boot_info.elf_sections()
                   .expect("ELF sections tag required!");

    kinfoln!(dots: " . ", "Detecting kernel ELF sections:");

    // Extract kernel ELF sections from  multiboot info
    let mut n_elf_sections = 0;

    let kernel_begin
        = elf_sections_tag.sections()
            .filter(|s| s.is_allocated())
            .map(|s| {
                kinfoln!( dots: " . . ", "{}", s );
                kinfoln!( dots: " . . . ", "flags: [ {:?} ]", s.flags());
                s.address() })
            .min()
            .expect("Could not find kernel start section!\
                    \nSomething is deeply wrong.");

    let kernel_end
        = elf_sections_tag.sections()
            .filter(|s| s.is_allocated())
            .map(|s| { n_elf_sections += 1; s.address() })
            .max()
            .expect("Could not find kernel end section!\
                    \nSomething is deeply wrong.");

    kinfoln!( dots: " . ", "Detected {} kernel ELF sections.", n_elf_sections);
    kinfoln!( dots: " . . ", "Kernel begins at {:#p} and ends at {:#p}."
            , kernel_begin, kernel_end );

    let multiboot_end = multiboot_addr + boot_info.length as u64;

    kinfoln!( dots: " . . ", "Multiboot info begins at {:#x} and ends at {:#x}."
            , multiboot_addr, multiboot_end);

    let params = InitParams { kernel_base: kernel_begin
                            , kernel_top: kernel_end
                            , multiboot_start: Some(multiboot_addr)
                            , multiboot_end: Some(multiboot_end)
                            , heap_base: unsafe { PAddr::from(HEAP_BASE) }
                            , heap_top: unsafe { PAddr::from(HEAP_TOP) }
                            , stack_base: unsafe { PAddr::from(STACK_BASE) }
                            , stack_top: unsafe { PAddr::from(STACK_TOP) }
                            , ..Default::default()
                        };

    // Extract the memory map tag from the multiboot info
    let mem_map = boot_info.mem_map()
                           .expect("Memory map tag required!");

    kinfoln!(dots: " . ", "Detected memory areas:");
    for area in mem_map {
        kinfoln!( dots: " . . ", "{}", area);
        params.mem_map.push(area.into());
    }

     // -- enable flags needed for paging ------------------------------------
     unsafe {
         control_regs::cr0::enable_write_protect(true);
         kinfoln!(dots: " . ", "Page write protect ENABED" );

         msr::enable_nxe();
         kinfoln!(dots: " . ", "Page no execute bit ENABLED");
     }

    kinfoln!(dots: " . ", "Transferring to `kernel_init()`.");
    ::kernel_init(&params);
}
<commit_msg>fix(x86_64::arch_init): fix missing mutability for InitParams<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! `x86_64` architecture-specific implementation.
// pub mod cpu;
pub mod drivers;
pub mod interrupts;

#[path = "../x86_all/bda.rs"] pub mod bda;
#[path = "../x86_all/multiboot2.rs"] pub mod multiboot2;

pub const ARCH_BITS: u8 = 64;

extern {
    // TODO: It would be really nice if there was a less ugly way of doing
    // this... (read: after the Revolution when we add memory regions to the
    // heap programmatically.)
    #[link_name = "heap_base_addr"]
    #[linkage = "external"]
    pub static HEAP_BASE: *mut u8;
    #[link_name = "heap_top_addr"]
    #[linkage = "external"]
    pub static HEAP_TOP: *mut u8;
    // Of course, we will still need to export the kernel stack addresses like
    // this, but it would be nice if they could be, i dont know, not mut u8s
    // pointers, like God intended.
    #[link_name = "stack_base"]
    pub static STACK_BASE: *mut u8;
    #[link_name = "stack_top"]
    pub static STACK_TOP: *mut u8;
}

use memory::PAddr;

/// Trampoline to ensure we have a correct stack frame for calling [`arch_init`]
///
/// I have no idea why this works, but it does.
///
/// [`arch_init`]: fn.arch_init
#[naked]
#[no_mangle]
pub unsafe extern "C" fn long_mode_init() {
    asm!("movabsq $$(stack_top), %rsp");
    asm!("mov ax, 0
          mov ss, ax
          mov ds, ax
          mov es, ax
          mov fs, ax
          mov gs, ax
          call arch_init"
        :::: "intel");

}

/// Entry point for architecture-specific kernel init
///
/// This expects to be passed the address of a valid
/// Multiboot 2 info struct. It's the bootloader's responsibility to ensure
/// that this is passed in the correct register as expected by the calling
/// convention (`edi` on x86). If this isn't there, you can expect to have a
/// bad problem and not go to space today.
#[no_mangle]
pub extern "C" fn arch_init(multiboot_addr: PAddr) {
    use cpu::{control_regs, msr};
    use elf;
    use params::InitParams;

    kinfoln!(dots: " . ", "Beginning `arch_init()` for x86_64");

    ::io::term::CONSOLE.lock().clear();
    ::logger::initialize()
        .expect("Could not initialize logger!");

    // -- Unpack multiboot tag ------------------------------------------------
    kinfoln!( dots: " . "
            , "trying to unpack multiboot info at {:#p}"
            , multiboot_addr);

    // try to interpret the structure at the multiboot address as a multiboot
    // info struct. if it's invalid, fail.
    let boot_info
        = unsafe { multiboot2::Info::from(multiboot_addr)
                    .expect("Could not unpack multiboot2 information!") };

    // Extract ELF sections tag from the multiboot info
    let elf_sections_tag
        = boot_info.elf_sections()
                   .expect("ELF sections tag required!");

    kinfoln!(dots: " . ", "Detecting kernel ELF sections:");

    // Extract kernel ELF sections from  multiboot info
    let mut n_elf_sections = 0;

    let kernel_begin
        = elf_sections_tag.sections()
            .filter(|s| s.is_allocated())
            .map(|s| {
                kinfoln!( dots: " . . ", "{}", s );
                kinfoln!( dots: " . . . ", "flags: [ {:?} ]", s.flags());
                s.address() })
            .min()
            .expect("Could not find kernel start section!\
                    \nSomething is deeply wrong.");

    let kernel_end
        = elf_sections_tag.sections()
            .filter(|s| s.is_allocated())
            .map(|s| { n_elf_sections += 1; s.address() })
            .max()
            .expect("Could not find kernel end section!\
                    \nSomething is deeply wrong.");

    kinfoln!( dots: " . ", "Detected {} kernel ELF sections.", n_elf_sections);
    kinfoln!( dots: " . . ", "Kernel begins at {:#p} and ends at {:#p}."
            , kernel_begin, kernel_end );

    let multiboot_end = multiboot_addr + boot_info.length as u64;

    kinfoln!( dots: " . . ", "Multiboot info begins at {:#x} and ends at {:#x}."
            , multiboot_addr, multiboot_end);

    let mut params = InitParams { kernel_base: kernel_begin
                            , kernel_top: kernel_end
                            , multiboot_start: Some(multiboot_addr)
                            , multiboot_end: Some(multiboot_end)
                            , heap_base: unsafe { PAddr::from(HEAP_BASE) }
                            , heap_top: unsafe { PAddr::from(HEAP_TOP) }
                            , stack_base: unsafe { PAddr::from(STACK_BASE) }
                            , stack_top: unsafe { PAddr::from(STACK_TOP) }
                            , ..Default::default()
                        };

    // Extract the memory map tag from the multiboot info
    let mem_map = boot_info.mem_map()
                           .expect("Memory map tag required!");

    kinfoln!(dots: " . ", "Detected memory areas:");
    for area in mem_map {
        kinfoln!( dots: " . . ", "{}", area);
        params.mem_map.push(area.into());
    }

     // -- enable flags needed for paging ------------------------------------
     unsafe {
         control_regs::cr0::enable_write_protect(true);
         kinfoln!(dots: " . ", "Page write protect ENABED" );

         msr::enable_nxe();
         kinfoln!(dots: " . ", "Page no execute bit ENABLED");
     }

    kinfoln!(dots: " . ", "Transferring to `kernel_init()`.");
    ::kernel_init(&params);
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate orbclient;
extern crate orbimage;

use std::env;

use orbclient::{Color, EventOption, Renderer, Window, WindowFlag};
use orbimage::Image;

#[derive(Clone, Copy)]
enum BackgroundMode {
    /// Do not resize the image, just center it
    Center,
    /// Resize the image to the display size
    Fill,
    /// Resize the image - keeping its aspect ratio, and fit it to the display with blank space
    Scale,
    /// Resize the image - keeping its aspect ratio, and crop to remove all blank space
    Zoom,
}

impl BackgroundMode {
    fn from_str(string: &str) -> BackgroundMode {
        match string {
            "fill" => BackgroundMode::Fill,
            "scale" => BackgroundMode::Scale,
            "zoom" => BackgroundMode::Zoom,
            _ => BackgroundMode::Center
        }
    }
}

fn find_scale(image: &Image, mode: BackgroundMode, display_width: u32, display_height: u32) -> (u32, u32) {
    match mode {
        BackgroundMode::Center => {
            (image.width(), image.height())
        },
        BackgroundMode::Fill => {
            (display_width, display_height)
        },
        BackgroundMode::Scale => {
            let d_w = display_width as f64;
            let d_h = display_height as f64;
            let i_w = image.width() as f64;
            let i_h = image.height() as f64;

            let scale = if d_w / d_h > i_w / i_h {
                d_h / i_h
            } else {
                d_w / i_w
            };

            ((i_w * scale) as u32, (i_h * scale) as u32)
        },
        BackgroundMode::Zoom => {
            let d_w = display_width as f64;
            let d_h = display_height as f64;
            let i_w = image.width() as f64;
            let i_h = image.height() as f64;

            let scale = if d_w / d_h < i_w / i_h {
                d_h / i_h
            } else {
                d_w / i_w
            };

            ((i_w * scale) as u32, (i_h * scale) as u32)
        }
    }
}

fn main() {
    let mut args = env::args().skip(1);

    let path = match args.next() {
        Some(arg) => arg,
        None => "/ui/background.png".to_string(),
    };

    let mode = BackgroundMode::from_str(&args.next().unwrap_or(String::new()));

    match Image::from_path(&path) {
        Ok(image) => {
            let (display_width, display_height) = orbclient::get_display_size().expect("viewer: failed to get display size");

            let mut window = Window::new_flags(
                0, 0, display_width, display_height, "",
                &[WindowFlag::Back, WindowFlag::Unclosable]
            ).unwrap();

            let mut scaled_image = image.clone();
            let mut resize = Some((display_width, display_height));
            loop {
                if let Some((w, h)) = resize.take() {
                    let (width, height) = find_scale(&image, mode, w, h);

                    if width == scaled_image.width() && height == scaled_image.height() {
                        // Do not resize scaled image
                    } else if width == image.width() && height == image.height() {
                        scaled_image = image.clone();
                    } else {
                        scaled_image = image.resize(width, height, orbimage::ResizeType::Lanczos3).unwrap();
                    }

                    window.set(Color::rgb(0, 0, 0));

                    let x = (window.width() as i32 - scaled_image.width() as i32)/2;
                    let y = (window.height() as i32 - scaled_image.height() as i32)/2;
                    scaled_image.draw(&mut window, x, y);

                    window.sync();
                }

                for event in window.events() {
                    match event.to_option() {
                        EventOption::Resize(resize_event) => {
                            resize = Some((resize_event.width, resize_event.height));
                        },
                        EventOption::Screen(screen_event) => {
                            window.set_size(screen_event.width, screen_event.height);
                            resize = Some((screen_event.width, screen_event.height));
                        },
                        EventOption::Quit(_) => return,
                        _ => ()
                    }
                }
            }
        },
        Err(err) => {
            println!("background: error loading {}: {}", path, err);
        }
    }
}
<commit_msg>background: Fix error message<commit_after>#![deny(warnings)]

extern crate orbclient;
extern crate orbimage;

use std::env;

use orbclient::{Color, EventOption, Renderer, Window, WindowFlag};
use orbimage::Image;

#[derive(Clone, Copy)]
enum BackgroundMode {
    /// Do not resize the image, just center it
    Center,
    /// Resize the image to the display size
    Fill,
    /// Resize the image - keeping its aspect ratio, and fit it to the display with blank space
    Scale,
    /// Resize the image - keeping its aspect ratio, and crop to remove all blank space
    Zoom,
}

impl BackgroundMode {
    fn from_str(string: &str) -> BackgroundMode {
        match string {
            "fill" => BackgroundMode::Fill,
            "scale" => BackgroundMode::Scale,
            "zoom" => BackgroundMode::Zoom,
            _ => BackgroundMode::Center
        }
    }
}

fn find_scale(image: &Image, mode: BackgroundMode, display_width: u32, display_height: u32) -> (u32, u32) {
    match mode {
        BackgroundMode::Center => {
            (image.width(), image.height())
        },
        BackgroundMode::Fill => {
            (display_width, display_height)
        },
        BackgroundMode::Scale => {
            let d_w = display_width as f64;
            let d_h = display_height as f64;
            let i_w = image.width() as f64;
            let i_h = image.height() as f64;

            let scale = if d_w / d_h > i_w / i_h {
                d_h / i_h
            } else {
                d_w / i_w
            };

            ((i_w * scale) as u32, (i_h * scale) as u32)
        },
        BackgroundMode::Zoom => {
            let d_w = display_width as f64;
            let d_h = display_height as f64;
            let i_w = image.width() as f64;
            let i_h = image.height() as f64;

            let scale = if d_w / d_h < i_w / i_h {
                d_h / i_h
            } else {
                d_w / i_w
            };

            ((i_w * scale) as u32, (i_h * scale) as u32)
        }
    }
}

fn main() {
    let mut args = env::args().skip(1);

    let path = match args.next() {
        Some(arg) => arg,
        None => "/ui/background.png".to_string(),
    };

    let mode = BackgroundMode::from_str(&args.next().unwrap_or(String::new()));

    match Image::from_path(&path) {
        Ok(image) => {
            let (display_width, display_height) = orbclient::get_display_size().expect("background: failed to get display size");

            let mut window = Window::new_flags(
                0, 0, display_width, display_height, "",
                &[WindowFlag::Back, WindowFlag::Unclosable]
            ).unwrap();

            let mut scaled_image = image.clone();
            let mut resize = Some((display_width, display_height));
            loop {
                if let Some((w, h)) = resize.take() {
                    let (width, height) = find_scale(&image, mode, w, h);

                    if width == scaled_image.width() && height == scaled_image.height() {
                        // Do not resize scaled image
                    } else if width == image.width() && height == image.height() {
                        scaled_image = image.clone();
                    } else {
                        scaled_image = image.resize(width, height, orbimage::ResizeType::Lanczos3).unwrap();
                    }

                    window.set(Color::rgb(0, 0, 0));

                    let x = (window.width() as i32 - scaled_image.width() as i32)/2;
                    let y = (window.height() as i32 - scaled_image.height() as i32)/2;
                    scaled_image.draw(&mut window, x, y);

                    window.sync();
                }

                for event in window.events() {
                    match event.to_option() {
                        EventOption::Resize(resize_event) => {
                            resize = Some((resize_event.width, resize_event.height));
                        },
                        EventOption::Screen(screen_event) => {
                            window.set_size(screen_event.width, screen_event.height);
                            resize = Some((screen_event.width, screen_event.height));
                        },
                        EventOption::Quit(_) => return,
                        _ => ()
                    }
                }
            }
        },
        Err(err) => {
            println!("background: error loading {}: {}", path, err);
        }
    }
}
<|endoftext|>


<commit_before>use ion_shell::{
    builtins::{man_pages::check_help, Status},
    sys::SIGTERM,
    types::Str,
    Shell,
};
use std::{error::Error, process::Command};

const MAN_EXEC: &str = r#"NAME
    exec - Replace the shell with the given command.

SYNOPSIS
    exec [-ch] [--help] [command [arguments ...]]

DESCRIPTION
    Execute <command>, replacing the shell with the specified program.
    The <arguments> following the command become the arguments to
    <command>.

OPTIONS
    -c  Execute command with an empty environment."#;

pub const MAN_EXIT: &str = r#"NAME
    exit - exit the shell

SYNOPSIS
    exit

DESCRIPTION
    Makes ion exit. The exit status will be that of the last command executed."#;

/// Executes the givent commmand.
pub fn _exec(args: &[Str]) -> Result<(), Str> {
    let mut clear_env = false;
    let mut idx = 0;
    for arg in args.iter() {
        match &**arg {
            "-c" => clear_env = true,
            _ if check_help(args, MAN_EXEC) => {
                return Ok(());
            }
            _ => break,
        }
        idx += 1;
    }

    match args.get(idx) {
        Some(argument) => {
            let args = if args.len() > idx + 1 { &args[idx + 1..] } else { &[] };
            let mut command = Command::new(argument.as_str());
            command.args(args.iter().map(Str::as_str));
            if clear_env {
                command.env_clear();
            }
            command.spawn().map(|_| ()).map_err(|err| err.description().into())
        }
        None => Err("no command provided".into()),
    }
}

pub fn exit(args: &[Str], shell: &mut Shell<'_>) -> Status {
    if check_help(args, MAN_EXIT) {
        return Status::SUCCESS;
    }
    // Kill all active background tasks before exiting the shell.
    shell.background_send(SIGTERM);
    let exit_code = args
        .get(1)
        .and_then(|status| status.parse::<i32>().ok())
        .unwrap_or_else(|| shell.previous_status().as_os_code());
    std::process::exit(exit_code);
}

pub fn exec(args: &[Str], _shell: &mut Shell<'_>) -> Status {
    match _exec(&args[1..]) {
        // Shouldn't ever hit this case.
        Ok(()) => unreachable!(),
        Err(err) => Status::error(format!("ion: exec: {}", err)),
    }
}
<commit_msg>Called spawn instead of exec :facepalm:<commit_after>use ion_shell::{
    builtins::{man_pages::check_help, Status},
    sys::SIGTERM,
    types::Str,
    Shell,
};
use std::{error::Error, os::unix::process::CommandExt, process::Command};

const MAN_EXEC: &str = r#"NAME
    exec - Replace the shell with the given command.

SYNOPSIS
    exec [-ch] [--help] [command [arguments ...]]

DESCRIPTION
    Execute <command>, replacing the shell with the specified program.
    The <arguments> following the command become the arguments to
    <command>.

OPTIONS
    -c  Execute command with an empty environment."#;

pub const MAN_EXIT: &str = r#"NAME
    exit - exit the shell

SYNOPSIS
    exit

DESCRIPTION
    Makes ion exit. The exit status will be that of the last command executed."#;

/// Executes the givent commmand.
pub fn _exec(args: &[Str]) -> Result<(), Str> {
    let mut clear_env = false;
    let mut idx = 0;
    for arg in args.iter() {
        match &**arg {
            "-c" => clear_env = true,
            _ if check_help(args, MAN_EXEC) => {
                return Ok(());
            }
            _ => break,
        }
        idx += 1;
    }

    match args.get(idx) {
        Some(argument) => {
            let args = if args.len() > idx + 1 { &args[idx + 1..] } else { &[] };
            let mut command = Command::new(argument.as_str());
            command.args(args.iter().map(Str::as_str));
            if clear_env {
                command.env_clear();
            }
            Err(command.exec().description().into())
        }
        None => Err("no command provided".into()),
    }
}

pub fn exit(args: &[Str], shell: &mut Shell<'_>) -> Status {
    if check_help(args, MAN_EXIT) {
        return Status::SUCCESS;
    }
    // Kill all active background tasks before exiting the shell.
    shell.background_send(SIGTERM);
    let exit_code = args
        .get(1)
        .and_then(|status| status.parse::<i32>().ok())
        .unwrap_or_else(|| shell.previous_status().as_os_code());
    std::process::exit(exit_code);
}

pub fn exec(args: &[Str], _shell: &mut Shell<'_>) -> Status {
    match _exec(&args[1..]) {
        // Shouldn't ever hit this case.
        Ok(()) => unreachable!(),
        Err(err) => Status::error(format!("ion: exec: {}", err)),
    }
}
<|endoftext|>