aboutsummaryrefslogtreecommitdiffstats
path: root/lib/lufa/Projects/MediaController/MediaController.h
blob: 0e8ed5d6f597c68a9a8d6f610a6d35e7b00651bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*
             LUFA Library
     Copyright (C) Dean Camera, 2017.

  dean [at] fourwalledcubicle [dot] com
           www.lufa-lib.org
*/

/*
  Copyright 2017  Dean Camera (dean [at] fourwalledcubicle [dot] com)

  Permission to use, copy, modify, distribute, and sell this
  software and its documentation for any purpose is hereby granted
  without fee, provided that the above copyright notice appear in
  all copies and that both that the copyright notice and this
  permission notice and warranty disclaimer appear in supporting
  documentation, and that the name of the author not be used in
  advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.

  The author disclaims all warranties with regard to this
  software, including all implied warranties of merchantability
  and fitness.  In no event shall the author be liable for any
  special, indirect or consequential damages or any damages
  whatsoever resulting from loss of use, data or profits, whether
  in an action of contract, negligence or other tortious action,
  arising out of or in connection with the use or performance of
  this software.
*/

/** \file
 *
 *  Header file for MediaControl.c.
 */

#ifndef _MEDIACONTROL_H_
#define _MEDIACONTROL_H_

	/* Includes: */
		#include <avr/io.h>
		#include <avr/wdt.h>
		#include <avr/power.h>
		#include <avr/interrupt.h>
		#include <stdbool.h>
		#include <string.h>

		#include "Descriptors.h"

		#include <LUFA/Drivers/Board/Joystick.h>
		#include <LUFA/Drivers/Board/LEDs.h>
		#include <LUFA/Drivers/Board/Buttons.h>
		#include <LUFA/Drivers/USB/USB.h>
		#include <LUFA/Platform/Platform.h>

	/* Macros: */
		/** LED mask for the library LED driver, to indicate that the USB interface is not ready. */
		#define LEDMASK_USB_NOTREADY      LEDS_LED1

		/** LED mask for the library LED driver, to indicate that the USB interface is enumerating. */
		#define LEDMASK_USB_ENUMERATING  (LEDS_LED2 | LEDS_LED3)

		/** LED mask for the library LED driver, to indicate that the USB interface is ready. */
		#define LEDMASK_USB_READY        (LEDS_LED2 | LEDS_LED4)

		/** LED mask for the library LED driver, to indicate that an error has occurred in the USB interface. */
		#define LEDMASK_USB_ERROR        (LEDS_LED1 | LEDS_LED3)

	/* Type Defines: */
		/** Type define for a Media Control HID report. This report contains the bits to match the usages defined
		 *  in the HID report of the device. When set to a true value, the relevant media controls on the host will
		 *  be triggered.
		 */
		typedef struct
		{
			unsigned Play           : 1;
			unsigned Pause          : 1;
			unsigned FForward       : 1;
			unsigned Rewind         : 1;
			unsigned NextTrack      : 1;
			unsigned PreviousTrack  : 1;
			unsigned Stop           : 1;
			unsigned PlayPause      : 1;
			unsigned Mute           : 1;
			unsigned VolumeUp       : 1;
			unsigned VolumeDown     : 1;
			unsigned RESERVED       : 5;
		} ATTR_PACKED USB_MediaReport_Data_t;

	/* Function Prototypes: */
		void SetupHardware(void);

		void EVENT_USB_Device_Connect(void);
		void EVENT_USB_Device_Disconnect(void);
		void EVENT_USB_Device_ConfigurationChanged(void);
		void EVENT_USB_Device_ControlRequest(void);
		void EVENT_USB_Device_StartOfFrame(void);

		bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
		                                         uint8_t* const ReportID,
		                                         const uint8_t ReportType,
		                                         void* ReportData,
		                                         uint16_t* const ReportSize);
		void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
		                                          const uint8_t ReportID,
		                                          const uint8_t ReportType,
		                                          const void* ReportData,
		                                          const uint16_t ReportSize);

#endif
hlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
import logging
import os
import json
from ctypes import byref
import libghdl
import libghdl.thin.errorout_memory as errorout_memory
import libghdl.thin.flags
import libghdl.thin.errorout as errorout
import libghdl.thin.files_map as files_map
import libghdl.thin.libraries as libraries
import libghdl.thin.name_table as name_table
import libghdl.thin.vhdl.nodes as nodes
import libghdl.thin.vhdl.lists as lists
import libghdl.thin.vhdl.std_package as std_package
import libghdl.thin.vhdl.parse
import libghdl.thin.vhdl.pyutils as pyutils
import libghdl.thin.vhdl.sem_lib as sem_lib

from . import lsp
from . import document, symbols

log = logging.getLogger(__name__)

class ProjectError(Exception):
    "Exception raised in case of unrecoverable error in the project file."
    def __init__(self, msg):
        super().__init__()
        self.msg = msg

class Workspace(object):
    def __init__(self, root_uri, server):
        self._root_uri = root_uri
        self._server = server
        self._root_path = lsp.path_from_uri(self._root_uri)
        self._docs = {}     # uri -> doc
        self._fe_map = {}   # fe -> doc
        self._prj = {}
        self._last_linted_doc = None
        errorout_memory.Install_Handler()
        libghdl.thin.flags.Flag_Elocations.value = True
        #thin.Flags.Verbose.value = True
        # We do analysis even in case of errors.
        libghdl.thin.vhdl.parse.Flag_Parse_Parenthesis.value = True
        # Force analysis to get more feedback + navigation even in case
        # of errors.
        libghdl.thin.flags.Flag_Force_Analysis.value = True
        # Do not consider analysis order issues.
        libghdl.thin.flags.Flag_Elaborate_With_Outdated.value = True
        libghdl.thin.errorout.Enable_Warning(errorout.Msgid.Warnid_Unused, True)
        self.read_project()
        self.set_options_from_project()
        libghdl.analyze_init()
        self._diags_set = set() # Documents with at least one diagnostic.
        self.read_files_from_project()
        self.gather_diagnostics(None)

    @property
    def documents(self):
        return self._docs

    @property
    def root_path(self):
        return self._root_path

    @property
    def root_uri(self):
        return self._root_uri

    def _create_document(self, doc_uri, sfe, version=None):
        """Create a document and put it in this workspace."""
        doc = document.Document(doc_uri, sfe, version)
        self._docs[doc_uri] = doc
        self._fe_map[sfe] = doc
        return doc

    def create_document_from_sfe(self, sfe, abspath):
        # A filename has been given without a corresponding document.
        # Create the document.
        # Common case: an error message was reported in a non-open document.
        #  Create a document so that it could be reported to the client.
        doc_uri = lsp.path_to_uri(os.path.normpath(abspath))
        return self._create_document(doc_uri, sfe)

    def create_document_from_uri(self, doc_uri, source=None, version=None):
        # A document is referenced by an uri but not known.  Load it.
        # We assume the path is correct.
        path = lsp.path_from_uri(doc_uri)
        if source is None:
            source = open(path).read()
        sfe = document.Document.load(source, os.path.dirname(path), os.path.basename(path))
        return self._create_document(doc_uri, sfe)

    def get_or_create_document(self, doc_uri):
        res = self.get_document(doc_uri)
        if res is not None:
            return res
        res = self.create_document_from_uri(doc_uri)
        res.parse_document()
        return res

    def get_document(self, doc_uri):
        """Get a document from :param doc_uri:  Note that the document may not exist,
        and this function may return None."""
        return self._docs.get(doc_uri)

    def put_document(self, doc_uri, source, version=None):
        doc = self.get_document(doc_uri)
        if doc is None:
            doc = self.create_document_from_uri(doc_uri, source=source, version=version)
        else:
            # The document may already be present (loaded from a project)
            # In that case, overwrite it as the client may have a more
            # recent version.
            doc.reload(source)
        return doc

    def sfe_to_document(self, sfe):
        """Get the document correspond to :param sfe: source file.
        Can create the document if needed."""
        assert sfe != 0
        doc = self._fe_map.get(sfe, None)
        if doc is None:
            # Could be a document from outside...
            filename = pyutils.name_image(files_map.Get_File_Name(sfe))
            if not os.path.isabs(filename):
                dirname = pyutils.name_image(files_map.Get_Directory_Name(sfe))
                filename = os.path.join(dirname, filename)
            doc = self.create_document_from_sfe(sfe, filename)
        return doc

    def add_vhdl_file(self, name):
        log.info("loading %s", name)
        if os.path.isabs(name):
            absname = name
        else:
            absname = os.path.join(self._root_path, name)
        # Create a document for this file.
        try:
            fd = open(absname)
            sfe = document.Document.load(fd.read(), self._root_path, name)
            fd.close()
        except OSError as err:
            self._server.show_message(
                lsp.MessageType.Error,
                "cannot load {}: {}".format(name, err.strerror))
            return
        doc = self.create_document_from_sfe(sfe, absname)
        doc.parse_document()

    def read_project(self):
        prj_file = os.path.join(self.root_path, 'hdl-prj.json')
        if not os.path.exists(prj_file):
            log.info("project file %s does not exist", prj_file)
            return
        try:
            f = open(prj_file)
        except OSError as err:
            self._server.show_message(
                lsp.MessageType.Error,
                "cannot open project file {}: {}".format(prj_file, err.strerror))
            return
        log.info("reading project file %s", prj_file)
        try:
            self._prj = json.load(f)
        except json.decoder.JSONDecodeError as e:
            log.info("error in project file")
            self._server.show_message(
                lsp.MessageType.Error,
                "json error in project file {}:{}:{}".format(
                    prj_file, e.lineno, e.colno))
        f.close()

    def set_options_from_project(self):
        try:
            if self._prj is None:
                return
            if not isinstance(self._prj, dict):
                raise ProjectError("project file is not a dictionnary")
            opts = self._prj.get('options', None)
            if opts is None:
                return
            if not isinstance(opts, dict):
                raise ProjectError("'options' is not a dictionnary")
            ghdl_opts = opts.get('ghdl_analysis', None)
            if ghdl_opts is None:
                return
            log.info("Using options: %s", ghdl_opts)
            for opt in ghdl_opts:
                if not libghdl.set_option(opt.encode('utf-8')):
                    self._server.show_message(lsp.MessageType.Error,
                        "error with option: {}".format(opt))
        except ProjectError as e:
            self._server.show_message(lsp.MessageType.Error,
                "error in project file: {}".format(e.msg))


    def read_files_from_project(self):
        try:
            files = self._prj.get('files', [])
            if not isinstance(files, list):
                raise ProjectError("'files' is not a list")
            for f in files:
                if not isinstance(f, dict):
                    raise ProjectError("an element of 'files' is not a dict")
                name = f.get('file')
                if not isinstance(name, str):
                    raise ProjectError("a 'file' is not a string")
                lang = f.get('language', 'vhdl')
                if lang == 'vhdl':
                    self.add_vhdl_file(name)
        except ProjectError as e:
            self._server.show_message(lsp.MessageType.Error,
                "error in project file: {}".format(e.msg))

    def get_configuration(self):
        self._server.configuration([{'scopeUri': '', 'section': 'vhdl.maxNumberOfProblems'}])

    def gather_diagnostics(self, doc):
        # Gather messages (per file)
        nbr_msgs = errorout_memory.Get_Nbr_Messages()
        diags = {}
        diag = {}
        for i in range(nbr_msgs):
            hdr = errorout_memory.Get_Error_Record(i+1)
            msg = errorout_memory.Get_Error_Message(i+1).decode('utf-8')
            if hdr.file == 0:
                # Possible for error limit reached.
                continue
            err_range = {
                'start': {'line': hdr.line - 1, 'character': hdr.offset},
                'end': {'line': hdr.line - 1,
                        'character': hdr.offset + hdr.length},
            }
            if hdr.group <= errorout_memory.Msg_Main:
                if hdr.id <= errorout.Msgid.Msgid_Note:
                    severity = lsp.DiagnosticSeverity.Information
                elif hdr.id <= errorout.Msgid.Msgid_Warning:
                    severity = lsp.DiagnosticSeverity.Warning
                else:
                    severity = lsp.DiagnosticSeverity.Error
                diag = {'source': 'ghdl',
                        'range': err_range,
                        'message': msg,
                        'severity': severity}
                if hdr.group == errorout_memory.Msg_Main:
                    diag['relatedInformation'] = []
                fdiag = diags.get(hdr.file, None)
                if fdiag is None:
                    diags[hdr.file] = [diag]
                else:
                    fdiag.append(diag)
            else:
                assert diag
                if True:
                    doc = self.sfe_to_document(hdr.file)
                    diag['relatedInformation'].append(
                        {'location': {'uri': doc.uri, 'range': err_range},
                         'message': msg})
        errorout_memory.Clear_Errors()
        # Publish diagnostics
        for sfe, diag in diags.items():
            doc = self.sfe_to_document(sfe)
            self.publish_diagnostics(doc.uri, diag)
        if doc is not None and doc._fe not in diags:
            # Clear previous diagnostics for the doc.
            self.publish_diagnostics(doc.uri, [])

    def obsolete_dependent_units(self, unit, antideps):
        """Obsolete units that depends of :param unit:"""
        udeps = antideps.get(unit, None)
        if udeps is None:
            # There are no units.
            return
        # Avoid infinite recursion
        antideps[unit] = None
        for un in udeps:
            log.debug("obsolete %d %s", un, pyutils.name_image(nodes.Get_Identifier(un)))
            # Recurse
            self.obsolete_dependent_units(un, antideps)
            if nodes.Set_Date_State(un) == nodes.Date_State.Disk:
                # Already obsolete!
                continue
            # FIXME: just de-analyze ?
            nodes.Set_Date_State(un, nodes.Date_State.Disk)
            sem_lib.Free_Dependence_List(un)
            loc = nodes.Get_Location(un)
            fil = files_map.Location_To_File(loc)
            pos = files_map.Location_File_To_Pos(loc, fil)
            line = files_map.Location_File_To_Line(loc, fil)
            col = files_map.Location_File_Line_To_Offset(loc, fil, line)
            nodes.Set_Design_Unit_Source_Pos(un, pos)
            nodes.Set_Design_Unit_Source_Line(un, line)
            nodes.Set_Design_Unit_Source_Col(un, col)

    def obsolete_doc(self, doc):
        if doc._tree == nodes.Null_Iir:
            return
        # Free old tree
        assert nodes.Get_Kind(doc._tree) == nodes.Iir_Kind.Design_File
        if self._last_linted_doc == doc:
            antideps = None
        else:
            antideps = self.compute_anti_dependences()
        unit = nodes.Get_First_Design_Unit(doc._tree)
        while unit != nodes.Null_Iir:
            if antideps is not None:
                self.obsolete_dependent_units(unit, antideps)
            # FIXME: free unit; it is not referenced.
            unit = nodes.Get_Chain(unit)
        libraries.Purge_Design_File(doc._tree)
        doc._tree = nodes.Null_Iir

    def lint(self, doc_uri):
        doc = self.get_document(doc_uri)
        self.obsolete_doc(doc)
        doc.compute_diags()
        self.gather_diagnostics(doc)

    def apply_changes(self, doc_uri, contentChanges, new_version):
        doc = self.get_document(doc_uri)
        assert doc is not None, 'try to modify a non-loaded document'
        self.obsolete_doc(doc)
        prev_sfe = doc._fe
        for change in contentChanges:
            doc.apply_change(change)
        if doc._fe != prev_sfe:
            del self._fe_map[prev_sfe]
            self._fe_map[doc._fe] = doc
        # Like lint
        doc.compute_diags()
        self.gather_diagnostics(doc)

    def check_document(self, doc_uri, source):
        self._docs[doc_uri].check_document(source)

    def rm_document(self, doc_uri):
        pass

    def apply_edit(self, edit):
        return self._server.request('workspace/applyEdit', {'edit': edit})

    def publish_diagnostics(self, doc_uri, diagnostics):
        self._server.notify('textDocument/publishDiagnostics',
                            params={'uri': doc_uri, 'diagnostics': diagnostics})

    def show_message(self, message, msg_type=lsp.MessageType.Info):
        self._server.notify('window/showMessage',
                            params={'type': msg_type, 'message': message})

    def declaration_to_location(self, decl):
        "Convert declaration :param decl: to an LSP Location"
        decl_loc = nodes.Get_Location(decl)
        if decl_loc == std_package.Std_Location.value:
            # There is no real file for the std.standard package.
            return None
        if decl_loc == libraries.Library_Location.value:
            # Libraries declaration are virtual.
            return None
        fe = files_map.Location_To_File(decl_loc)
        doc = self.sfe_to_document(fe)
        res = {'uri': doc.uri}
        nid = nodes.Get_Identifier(decl)
        res['range'] = {'start': symbols.location_to_position(fe, decl_loc),
                        'end': symbols.location_to_position(fe, decl_loc + name_table.Get_Name_Length(nid))}
        return res

    def goto_definition(self, doc_uri, position):
        decl = self._docs[doc_uri].goto_definition(position)
        if decl is None:
            return None
        decl_loc = self.declaration_to_location(decl)
        if decl_loc is None:
            return None
        res = [decl_loc]
        if nodes.Get_Kind(decl) == nodes.Iir_Kind.Component_Declaration:
            ent = libraries.Find_Entity_For_Component(nodes.Get_Identifier(decl))
            if ent != nodes.Null_Iir:
                res.append(self.declaration_to_location(nodes.Get_Library_Unit(ent)))
        return res

    def x_show_all_files(self):
        res = []
        for fe in range(1, files_map.Get_Last_Source_File_Entry() + 1):
            doc = self._fe_map.get(fe, None)
            res.append({'fe': fe,
                        'uri': doc.uri if doc is not None else None,
                        'name': pyutils.name_image(files_map.Get_File_Name(fe)),
                        'dir': pyutils.name_image(files_map.Get_Directory_Name(fe))})
        return res

    def x_get_all_entities(self):
        res = []
        lib = libraries.Get_Libraries_Chain()
        while lib != nodes.Null_Iir:
            files = nodes.Get_Design_File_Chain(lib)
            ents = []
            while files != nodes.Null_Iir:
                units = nodes.Get_First_Design_Unit(files)
                while units != nodes.Null_Iir:
                    unitlib = nodes.Get_Library_Unit(units)
                    if nodes.Get_Kind(unitlib) == nodes.Iir_Kind.Entity_Declaration:
                        ents.append(unitlib)
                    units = nodes.Get_Chain(units)
                files = nodes.Get_Chain(files)
            ents = [pyutils.name_image(nodes.Get_Identifier(e)) for e in ents]
            lib_name = pyutils.name_image(nodes.Get_Identifier(lib))
            res.extend([{'name': n, 'library': lib_name} for n in ents])
            lib = nodes.Get_Chain(lib)
        return res

    def x_get_entity_interface(self, library, name):
        def create_interfaces(inters):
            res = []
            while inters != nodes.Null_Iir:
                res.append({'name': name_table.Get_Name_Ptr(nodes.Get_Identifier(inters)).decode('latin-1')})
                inters = nodes.Get_Chain(inters)
            return res
        # Find library
        lib_id = name_table.Get_Identifier(library.encode('utf-8'))
        lib = libraries.Get_Library_No_Create(lib_id)
        if lib == name_table.Null_Identifier:
            return None
        # Find entity
        ent_id = name_table.Get_Identifier(name.encode('utf-8'))
        unit = libraries.Find_Primary_Unit(lib, ent_id)
        if unit == nodes.Null_Iir:
            return None
        ent = nodes.Get_Library_Unit(unit)
        return {'library': library,
                'entity': name,
                'generics': create_interfaces(nodes.Get_Generic_Chain(ent)),
                'ports': create_interfaces(nodes.Get_Port_Chain(ent))}


    def compute_anti_dependences(self):
        """Return a dictionnary of anti dependencies for design unit"""
        res = {}
        lib = libraries.Get_Libraries_Chain()
        while lib != nodes.Null_Iir:
            files = nodes.Get_Design_File_Chain(lib)
            while files != nodes.Null_Iir:
                units = nodes.Get_First_Design_Unit(files)
                while units != nodes.Null_Iir:
                    if nodes.Get_Date_State(units) == nodes.Date_State.Analyze:
                        # The unit has been analyzed, so the dependencies are know.
                        deps = nodes.Get_Dependence_List(units)
                        assert deps != nodes.Null_Iir_List
                        deps_it = lists.Iterate(deps)
                        while lists.Is_Valid(byref(deps_it)):
                            el = lists.Get_Element(byref(deps_it))
                            if nodes.Get_Kind(el) == nodes.Iir_Kind.Design_Unit:
                                if res.get(el, None):
                                    res[el].append(units)
                                else:
                                    res[el] = [units]
                            else:
                                assert False
                            lists.Next(byref(deps_it))
                    units = nodes.Get_Chain(units)
                files = nodes.Get_Chain(files)
            lib = nodes.Get_Chain(lib)
        return res